@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.js
CHANGED
|
@@ -29,6 +29,12 @@ var LATER_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
|
29
29
|
"lambda",
|
|
30
30
|
"class_definition"
|
|
31
31
|
]);
|
|
32
|
+
function isModule(node) {
|
|
33
|
+
return node.type === "module";
|
|
34
|
+
}
|
|
35
|
+
function runsAtModuleLoad(node) {
|
|
36
|
+
return !LATER_BODY_TYPES.has(node.type) && node.type !== "decorator";
|
|
37
|
+
}
|
|
32
38
|
function enclosingFunction(node) {
|
|
33
39
|
let current = node.parent;
|
|
34
40
|
while (current !== null) {
|
|
@@ -724,6 +730,15 @@ import {
|
|
|
724
730
|
function resolveCalls(db, callKeys) {
|
|
725
731
|
askResolution(db, callKeys);
|
|
726
732
|
}
|
|
733
|
+
function resolveEnvObjects(db, objectKeys) {
|
|
734
|
+
askResolution(db, objectKeys, "wantedEnvObject");
|
|
735
|
+
}
|
|
736
|
+
function resolvedFunctions(db, key) {
|
|
737
|
+
return [
|
|
738
|
+
...db.lookup("wantedResolves", 0, key),
|
|
739
|
+
...db.lookup("wantedGivesBack", 0, key)
|
|
740
|
+
].map((row) => String(row[1]));
|
|
741
|
+
}
|
|
727
742
|
function writtenValueUnder(db, key, site) {
|
|
728
743
|
if (askResolutionUnder(db, [[key, site]]) === "abandoned") {
|
|
729
744
|
return null;
|
|
@@ -742,6 +757,13 @@ function writtenValuesOf(db, key) {
|
|
|
742
757
|
resolveCalls(db, [key]);
|
|
743
758
|
return sharedWrittenValuesOf(db, key, (keys) => resolveCalls(db, keys));
|
|
744
759
|
}
|
|
760
|
+
function settleWrittenValues(db, keys) {
|
|
761
|
+
if (keys.length === 0) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
resolveCalls(db, keys);
|
|
765
|
+
writtenValuesByKey(db, keys, (behind) => resolveCalls(db, behind));
|
|
766
|
+
}
|
|
745
767
|
function originsOf(db, nameKey2) {
|
|
746
768
|
askResolution(db, [nameKey2], "wantedOrigin");
|
|
747
769
|
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey2).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
@@ -936,31 +958,46 @@ function emitCall(emitter, call) {
|
|
|
936
958
|
if (!emitter.insideMethod) {
|
|
937
959
|
add(emitter, "callOutsideMethod", callKey);
|
|
938
960
|
}
|
|
961
|
+
for (const argument of callArguments(call)) {
|
|
962
|
+
if (argument.kind === "keyword") {
|
|
963
|
+
add(
|
|
964
|
+
emitter,
|
|
965
|
+
"callKeywordArg",
|
|
966
|
+
callKey,
|
|
967
|
+
argument.name,
|
|
968
|
+
valueKey(emitter, argument.node)
|
|
969
|
+
);
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
add(
|
|
973
|
+
emitter,
|
|
974
|
+
"callArg",
|
|
975
|
+
callKey,
|
|
976
|
+
String(argument.position),
|
|
977
|
+
valueKey(emitter, argument.node)
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function callArguments(call) {
|
|
982
|
+
const args = field(call, "arguments");
|
|
983
|
+
if (args === null) {
|
|
984
|
+
return [];
|
|
985
|
+
}
|
|
986
|
+
const written = [];
|
|
939
987
|
let position = 0;
|
|
940
988
|
for (const argument of children(args)) {
|
|
941
989
|
if (argument.type === "keyword_argument") {
|
|
942
990
|
const name = field(argument, "name");
|
|
943
991
|
const value = field(argument, "value");
|
|
944
992
|
if (name !== null && value !== null) {
|
|
945
|
-
|
|
946
|
-
emitter,
|
|
947
|
-
"callKeywordArg",
|
|
948
|
-
callKey,
|
|
949
|
-
name.text,
|
|
950
|
-
valueKey(emitter, value)
|
|
951
|
-
);
|
|
993
|
+
written.push({ kind: "keyword", name: name.text, node: value });
|
|
952
994
|
}
|
|
953
995
|
continue;
|
|
954
996
|
}
|
|
955
|
-
|
|
956
|
-
emitter,
|
|
957
|
-
"callArg",
|
|
958
|
-
callKey,
|
|
959
|
-
String(position),
|
|
960
|
-
valueKey(emitter, argument)
|
|
961
|
-
);
|
|
997
|
+
written.push({ kind: "positional", position, node: argument });
|
|
962
998
|
position += 1;
|
|
963
999
|
}
|
|
1000
|
+
return written;
|
|
964
1001
|
}
|
|
965
1002
|
function valueKey(emitter, value) {
|
|
966
1003
|
if (value.type !== "identifier") {
|
|
@@ -1027,9 +1064,51 @@ function walkExpressions(emitter, node, visit) {
|
|
|
1027
1064
|
walkExpressions(emitter, child, visit);
|
|
1028
1065
|
}
|
|
1029
1066
|
}
|
|
1067
|
+
var WRITTEN_KEY_TYPES = /* @__PURE__ */ new Set(["string", "integer", "concatenated_string"]);
|
|
1068
|
+
function emitKeyedRead(emitter, site, container, key) {
|
|
1069
|
+
if (WRITTEN_KEY_TYPES.has(key.type)) {
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
add(
|
|
1073
|
+
emitter,
|
|
1074
|
+
"readsKeyed",
|
|
1075
|
+
nodeId(emitter.filePath, site),
|
|
1076
|
+
valueKey(emitter, container),
|
|
1077
|
+
valueKey(emitter, key)
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
function singleSubscript(node) {
|
|
1081
|
+
const written = fields(node, "subscript");
|
|
1082
|
+
return written.length === 1 ? written[0] ?? null : null;
|
|
1083
|
+
}
|
|
1084
|
+
function mappingGetRead(call) {
|
|
1085
|
+
const callee = field(call, "function");
|
|
1086
|
+
if (callee === null || callee.type !== "attribute") {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
const container = field(callee, "object");
|
|
1090
|
+
if (container === null || field(callee, "attribute")?.text !== "get") {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
const first = callArguments(call).find(
|
|
1094
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
1095
|
+
);
|
|
1096
|
+
return first === void 0 ? null : { container, key: first.node };
|
|
1097
|
+
}
|
|
1030
1098
|
function emitExpressionFact(emitter, child) {
|
|
1031
1099
|
if (child.type === "call") {
|
|
1032
1100
|
emitCall(emitter, child);
|
|
1101
|
+
const mapping = mappingGetRead(child);
|
|
1102
|
+
if (mapping !== null) {
|
|
1103
|
+
emitKeyedRead(emitter, child, mapping.container, mapping.key);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
if (child.type === "subscript") {
|
|
1107
|
+
const index = singleSubscript(child);
|
|
1108
|
+
const container = field(child, "value");
|
|
1109
|
+
if (index !== null && container !== null) {
|
|
1110
|
+
emitKeyedRead(emitter, child, container, index);
|
|
1111
|
+
}
|
|
1033
1112
|
}
|
|
1034
1113
|
if (child.type === "dictionary") {
|
|
1035
1114
|
emitDictionary(emitter, child);
|
|
@@ -1097,6 +1176,9 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1097
1176
|
return funcKey;
|
|
1098
1177
|
}
|
|
1099
1178
|
emitNestedDefinitions(inside, body);
|
|
1179
|
+
if (fn.type === "lambda") {
|
|
1180
|
+
emitExpressionFact(inside, body);
|
|
1181
|
+
}
|
|
1100
1182
|
let statesReturn = false;
|
|
1101
1183
|
const stores = /* @__PURE__ */ new Map();
|
|
1102
1184
|
walkExpressions(inside, body, (child) => {
|
|
@@ -2476,6 +2558,10 @@ function writtenNodeOf(node, db) {
|
|
|
2476
2558
|
const bound = db === void 0 ? void 0 : projects.get(db);
|
|
2477
2559
|
return bound?.context.writtenTo(node) ?? null;
|
|
2478
2560
|
}
|
|
2561
|
+
function resolutionKeyOf(node, db) {
|
|
2562
|
+
const bound = db === void 0 ? void 0 : projects.get(db);
|
|
2563
|
+
return bound?.keyOf(node) ?? null;
|
|
2564
|
+
}
|
|
2479
2565
|
var NOTHING_BUILT = { type: "noCall" };
|
|
2480
2566
|
function constructionBehind(node, db) {
|
|
2481
2567
|
if (db === void 0) {
|
|
@@ -2510,8 +2596,17 @@ function askWrittenValues(nodes, db) {
|
|
|
2510
2596
|
if (bound === void 0) {
|
|
2511
2597
|
return;
|
|
2512
2598
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2599
|
+
settleWrittenValues(db, resolutionKeysOf(nodes, bound));
|
|
2600
|
+
}
|
|
2601
|
+
function resolutionKeysOf(nodes, bound) {
|
|
2602
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2603
|
+
for (const node of nodes) {
|
|
2604
|
+
const key = bound.keyOf(node);
|
|
2605
|
+
if (key !== null) {
|
|
2606
|
+
keys.add(key);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
return [...keys];
|
|
2515
2610
|
}
|
|
2516
2611
|
function evaluatedValue(node, db, site) {
|
|
2517
2612
|
const evaluator = evaluatorFor(node, db);
|
|
@@ -2579,7 +2674,7 @@ function projectOver(db, nodes) {
|
|
|
2579
2674
|
filesByRoot.set(entry.root.id, entry);
|
|
2580
2675
|
rootsByFile.set(entry.file, entry.root);
|
|
2581
2676
|
}
|
|
2582
|
-
const
|
|
2677
|
+
const keyOf3 = (node) => {
|
|
2583
2678
|
const entry = filesByRoot.get(node.tree.rootNode.id);
|
|
2584
2679
|
if (entry === void 0) {
|
|
2585
2680
|
return null;
|
|
@@ -2588,7 +2683,7 @@ function projectOver(db, nodes) {
|
|
|
2588
2683
|
};
|
|
2589
2684
|
const context = {
|
|
2590
2685
|
writtenTo: (node, site) => {
|
|
2591
|
-
const key =
|
|
2686
|
+
const key = keyOf3(node);
|
|
2592
2687
|
if (key === null) {
|
|
2593
2688
|
return null;
|
|
2594
2689
|
}
|
|
@@ -2597,17 +2692,17 @@ function projectOver(db, nodes) {
|
|
|
2597
2692
|
},
|
|
2598
2693
|
callable: (call) => {
|
|
2599
2694
|
const callee = field(call, "function");
|
|
2600
|
-
const key = callee === null ? null :
|
|
2695
|
+
const key = callee === null ? null : keyOf3(callee);
|
|
2601
2696
|
if (key === null) {
|
|
2602
2697
|
return null;
|
|
2603
2698
|
}
|
|
2604
2699
|
resolveCalls(db, [key]);
|
|
2605
|
-
const resolved = db.
|
|
2606
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2700
|
+
const resolved = db.lookup("wantedResolves", 0, key);
|
|
2701
|
+
const settled = resolved.length === 1 ? String(resolved[0]?.[1]) : void 0;
|
|
2607
2702
|
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
2608
2703
|
}
|
|
2609
2704
|
};
|
|
2610
|
-
return { keyOf:
|
|
2705
|
+
return { keyOf: keyOf3, roots: rootsByFile, context };
|
|
2611
2706
|
}
|
|
2612
2707
|
|
|
2613
2708
|
// src/decorators.ts
|
|
@@ -3245,6 +3340,18 @@ function bodyCalls(node, found = []) {
|
|
|
3245
3340
|
}
|
|
3246
3341
|
return found;
|
|
3247
3342
|
}
|
|
3343
|
+
function moduleLoadCalls(node, found = []) {
|
|
3344
|
+
for (const child of node.namedChildren) {
|
|
3345
|
+
if (child === null || !runsAtModuleLoad(child)) {
|
|
3346
|
+
continue;
|
|
3347
|
+
}
|
|
3348
|
+
if (child.type === "call") {
|
|
3349
|
+
found.push(child);
|
|
3350
|
+
}
|
|
3351
|
+
moduleLoadCalls(child, found);
|
|
3352
|
+
}
|
|
3353
|
+
return found;
|
|
3354
|
+
}
|
|
3248
3355
|
function enclosingStatement(call, body) {
|
|
3249
3356
|
let current = call;
|
|
3250
3357
|
while (current !== null && current.parent !== null) {
|
|
@@ -3269,15 +3376,18 @@ function argOf(node, facts) {
|
|
|
3269
3376
|
}
|
|
3270
3377
|
return { kind: "identifier", name: node.text };
|
|
3271
3378
|
}
|
|
3272
|
-
function
|
|
3379
|
+
function argumentNodes(call) {
|
|
3273
3380
|
const args = field(call, "arguments");
|
|
3274
3381
|
if (args === null) {
|
|
3275
3382
|
return [];
|
|
3276
3383
|
}
|
|
3277
3384
|
return args.namedChildren.filter((child) => child !== null).map(
|
|
3278
|
-
(child) => child.type === "keyword_argument" ?
|
|
3385
|
+
(child) => child.type === "keyword_argument" ? field(child, "value") ?? child : child
|
|
3279
3386
|
);
|
|
3280
3387
|
}
|
|
3388
|
+
function argsOf(call, facts) {
|
|
3389
|
+
return argumentNodes(call).map((node) => argOf(node, facts));
|
|
3390
|
+
}
|
|
3281
3391
|
function calleeText(call) {
|
|
3282
3392
|
return field(call, "function")?.text ?? call.text;
|
|
3283
3393
|
}
|
|
@@ -3286,7 +3396,40 @@ function invocationEffects(definitionNode, facts) {
|
|
|
3286
3396
|
if (body === null) {
|
|
3287
3397
|
return [];
|
|
3288
3398
|
}
|
|
3289
|
-
|
|
3399
|
+
return invocationEffectsIn(body, bodyCalls(body), facts);
|
|
3400
|
+
}
|
|
3401
|
+
function moduleLoadInvocationEffects(moduleNode, facts) {
|
|
3402
|
+
return invocationEffectsIn(moduleNode, moduleLoadCalls(moduleNode), facts);
|
|
3403
|
+
}
|
|
3404
|
+
var ASKED_ABOUT_TYPES = /* @__PURE__ */ new Set(["identifier", "attribute", "call"]);
|
|
3405
|
+
function askedNodesUnder(node, found = []) {
|
|
3406
|
+
if (ASKED_ABOUT_TYPES.has(node.type)) {
|
|
3407
|
+
found.push(node);
|
|
3408
|
+
}
|
|
3409
|
+
for (const child of node.namedChildren) {
|
|
3410
|
+
if (child !== null && !NESTED_DEFINITION_TYPES.has(child.type)) {
|
|
3411
|
+
askedNodesUnder(child, found);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
return found;
|
|
3415
|
+
}
|
|
3416
|
+
function bodyValueNodes(definitionNode) {
|
|
3417
|
+
const body = field(definitionNode, "body");
|
|
3418
|
+
return body === null ? [] : askedNodesUnder(body);
|
|
3419
|
+
}
|
|
3420
|
+
function effectValueNodes(call, gating) {
|
|
3421
|
+
const found = argumentNodes(call).flatMap((node) => askedNodesUnder(node));
|
|
3422
|
+
for (const path6 of gating ?? []) {
|
|
3423
|
+
for (const condition of path6) {
|
|
3424
|
+
if (condition.expression !== null) {
|
|
3425
|
+
askedNodesUnder(condition.expression, found);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
return found;
|
|
3430
|
+
}
|
|
3431
|
+
function invocationEffectsIn(body, written, facts) {
|
|
3432
|
+
const calls = [...written].sort((a, b) => a.endIndex - b.endIndex);
|
|
3290
3433
|
if (calls.length === 0) {
|
|
3291
3434
|
return [];
|
|
3292
3435
|
}
|
|
@@ -3308,10 +3451,17 @@ function invocationEffects(definitionNode, facts) {
|
|
|
3308
3451
|
},
|
|
3309
3452
|
statements
|
|
3310
3453
|
);
|
|
3311
|
-
|
|
3454
|
+
const gatingOf = calls.map((call) => {
|
|
3312
3455
|
const statement = statementOf3.get(call.id);
|
|
3456
|
+
return statement === void 0 ? void 0 : enumerated.byTerminal.get(statement);
|
|
3457
|
+
});
|
|
3458
|
+
askWrittenValues(
|
|
3459
|
+
calls.flatMap((call, index) => effectValueNodes(call, gatingOf[index])),
|
|
3460
|
+
facts
|
|
3461
|
+
);
|
|
3462
|
+
return calls.map((call, index) => {
|
|
3313
3463
|
const conditions = sharedGatingConditions(
|
|
3314
|
-
|
|
3464
|
+
gatingOf[index],
|
|
3315
3465
|
(condition) => predicateOf(condition, facts)
|
|
3316
3466
|
);
|
|
3317
3467
|
return {
|
|
@@ -3342,6 +3492,22 @@ function clientCallUnits(root, module, pack, pattern, options) {
|
|
|
3342
3492
|
}
|
|
3343
3493
|
return units;
|
|
3344
3494
|
}
|
|
3495
|
+
function clientCallReceivers(root, module) {
|
|
3496
|
+
const found = [];
|
|
3497
|
+
for (const definition of functionDefinitions(root)) {
|
|
3498
|
+
for (const call of bodyCalls(definition)) {
|
|
3499
|
+
const callee = field(call, "function");
|
|
3500
|
+
if (callee === null || originOf(callee, module) !== null) {
|
|
3501
|
+
continue;
|
|
3502
|
+
}
|
|
3503
|
+
const object = field(callee, "object");
|
|
3504
|
+
if (object !== null && field(callee, "attribute") !== null) {
|
|
3505
|
+
found.push(object);
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return found;
|
|
3510
|
+
}
|
|
3345
3511
|
function functionDefinitions(node, found = []) {
|
|
3346
3512
|
for (const child of children(node)) {
|
|
3347
3513
|
if (isFunction(child)) {
|
|
@@ -3550,14 +3716,22 @@ import { runtimeConfigBinding } from "@suss/behavioral-ir";
|
|
|
3550
3716
|
import { SKIP_CHILDREN, walkDescendants } from "@suss/extractor";
|
|
3551
3717
|
var PYTHON_ENV_RECOGNITION = "python-env";
|
|
3552
3718
|
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
3553
|
-
function envReadEffects(root, module) {
|
|
3554
|
-
const
|
|
3719
|
+
function envReadEffects(root, module, facts) {
|
|
3720
|
+
const slots = [];
|
|
3721
|
+
const readsHelpers = facts !== void 0 && sitesByDb.has(facts);
|
|
3555
3722
|
const startScope = module.scopeFor.get(root.id) ?? module.moduleScope;
|
|
3556
3723
|
walkDescendants(root, startScope, {
|
|
3557
3724
|
at: (node, scope) => {
|
|
3558
|
-
const
|
|
3559
|
-
if (
|
|
3560
|
-
|
|
3725
|
+
const syntax = envReadSyntaxAt(node, scope);
|
|
3726
|
+
if (syntax !== null) {
|
|
3727
|
+
const name = stringLiteralValue(syntax.name);
|
|
3728
|
+
if (name !== null) {
|
|
3729
|
+
slots.push({ read: { name, defaulted: syntax.defaulted } });
|
|
3730
|
+
}
|
|
3731
|
+
return;
|
|
3732
|
+
}
|
|
3733
|
+
if (readsHelpers && node.type === "call") {
|
|
3734
|
+
slots.push({ call: node });
|
|
3561
3735
|
}
|
|
3562
3736
|
},
|
|
3563
3737
|
into: (node, scope) => {
|
|
@@ -3567,7 +3741,16 @@ function envReadEffects(root, module) {
|
|
|
3567
3741
|
return module.scopeFor.get(node.id) ?? scope;
|
|
3568
3742
|
}
|
|
3569
3743
|
});
|
|
3570
|
-
|
|
3744
|
+
const throughHelpers = helperReadsByCall(
|
|
3745
|
+
slots.flatMap((slot) => "call" in slot ? [slot.call] : []),
|
|
3746
|
+
facts
|
|
3747
|
+
);
|
|
3748
|
+
return slots.flatMap((slot) => {
|
|
3749
|
+
if ("read" in slot) {
|
|
3750
|
+
return [configReadEffect(slot.read)];
|
|
3751
|
+
}
|
|
3752
|
+
return throughHelpers.get(slot.call.id) ?? [];
|
|
3753
|
+
});
|
|
3571
3754
|
}
|
|
3572
3755
|
function configReadEffect(read) {
|
|
3573
3756
|
return {
|
|
@@ -3585,7 +3768,7 @@ function configReadEffect(read) {
|
|
|
3585
3768
|
}
|
|
3586
3769
|
};
|
|
3587
3770
|
}
|
|
3588
|
-
function
|
|
3771
|
+
function envReadSyntaxAt(node, scope) {
|
|
3589
3772
|
if (node.type === "subscript") {
|
|
3590
3773
|
return subscriptRead(node, scope);
|
|
3591
3774
|
}
|
|
@@ -3600,11 +3783,10 @@ function subscriptRead(node, scope) {
|
|
|
3600
3783
|
if (value === null || index === null || !isEnviron(value, scope)) {
|
|
3601
3784
|
return null;
|
|
3602
3785
|
}
|
|
3603
|
-
|
|
3604
|
-
if (name === null || isAssignedTo(node)) {
|
|
3786
|
+
if (isAssignedTo(node)) {
|
|
3605
3787
|
return null;
|
|
3606
3788
|
}
|
|
3607
|
-
return { name, defaulted: isDefaultedAt(node) };
|
|
3789
|
+
return { name: index, defaulted: isDefaultedAt(node) };
|
|
3608
3790
|
}
|
|
3609
3791
|
function isAssignedTo(node) {
|
|
3610
3792
|
const parent = node.parent;
|
|
@@ -3618,22 +3800,20 @@ function isAssignedTo(node) {
|
|
|
3618
3800
|
}
|
|
3619
3801
|
function callRead(node, scope) {
|
|
3620
3802
|
const callee = field(node, "function");
|
|
3621
|
-
|
|
3622
|
-
if (callee === null || args === null || !isEnvGetter(callee, scope)) {
|
|
3803
|
+
if (callee === null || !isEnvGetter(callee, scope)) {
|
|
3623
3804
|
return null;
|
|
3624
3805
|
}
|
|
3625
|
-
const
|
|
3626
|
-
|
|
3806
|
+
const written = callArguments(node);
|
|
3807
|
+
const name = written.find(
|
|
3808
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
3627
3809
|
);
|
|
3628
|
-
|
|
3629
|
-
if (name === null) {
|
|
3810
|
+
if (name === void 0) {
|
|
3630
3811
|
return null;
|
|
3631
3812
|
}
|
|
3632
|
-
const
|
|
3633
|
-
(
|
|
3813
|
+
const hasDefault = written.some(
|
|
3814
|
+
(argument) => argument.kind === "positional" && argument.position === 1 || argument.kind === "keyword" && argument.name === "default"
|
|
3634
3815
|
);
|
|
3635
|
-
|
|
3636
|
-
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3816
|
+
return { name: name.node, defaulted: hasDefault || isDefaultedAt(node) };
|
|
3637
3817
|
}
|
|
3638
3818
|
function isEnvGetter(callee, scope) {
|
|
3639
3819
|
if (callee.type === "attribute") {
|
|
@@ -3690,6 +3870,194 @@ function isDefaultedAt(node) {
|
|
|
3690
3870
|
}
|
|
3691
3871
|
return false;
|
|
3692
3872
|
}
|
|
3873
|
+
function envFactsIn(filePath, root, module) {
|
|
3874
|
+
const found = { sites: [], objects: [] };
|
|
3875
|
+
walkDescendants(root, module.moduleScope, {
|
|
3876
|
+
at: (node, scope) => {
|
|
3877
|
+
if (isEnviron(node, scope) && handsOnward(node)) {
|
|
3878
|
+
found.objects.push(keyOf(filePath, node));
|
|
3879
|
+
}
|
|
3880
|
+
const syntax = envReadSyntaxAt(node, scope);
|
|
3881
|
+
if (syntax === null || stringLiteralValue(syntax.name) !== null) {
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
found.sites.push({
|
|
3885
|
+
site: nodeId(filePath, node),
|
|
3886
|
+
nameKey: keyOf(filePath, syntax.name),
|
|
3887
|
+
defaulted: syntax.defaulted,
|
|
3888
|
+
functionKey: functionReadKey(filePath, node, scope)
|
|
3889
|
+
});
|
|
3890
|
+
},
|
|
3891
|
+
into: (node, scope) => module.scopeFor.get(node.id) ?? scope
|
|
3892
|
+
});
|
|
3893
|
+
return found;
|
|
3894
|
+
}
|
|
3895
|
+
var keyOf = (filePath, node) => readKey(filePath, node, enclosingFunction(node));
|
|
3896
|
+
function functionReadKey(filePath, node, scope) {
|
|
3897
|
+
if (node.type !== "call") {
|
|
3898
|
+
return null;
|
|
3899
|
+
}
|
|
3900
|
+
const callee = field(node, "function");
|
|
3901
|
+
if (callee === null || isEnviron(field(callee, "object") ?? callee, scope)) {
|
|
3902
|
+
return null;
|
|
3903
|
+
}
|
|
3904
|
+
return keyOf(filePath, callee);
|
|
3905
|
+
}
|
|
3906
|
+
function handsOnward(node) {
|
|
3907
|
+
const parent = node.parent;
|
|
3908
|
+
if (parent === null) {
|
|
3909
|
+
return true;
|
|
3910
|
+
}
|
|
3911
|
+
if (parent.type === "subscript" && field(parent, "value")?.id === node.id) {
|
|
3912
|
+
return !writesTheKey(field(parent, "subscript"));
|
|
3913
|
+
}
|
|
3914
|
+
if (parent.type === "attribute" && field(parent, "object")?.id === node.id) {
|
|
3915
|
+
const call = parent.parent;
|
|
3916
|
+
return field(parent, "attribute")?.text === "get" && call !== null && call.type === "call" && !writesTheKey(firstArgumentOf(call));
|
|
3917
|
+
}
|
|
3918
|
+
return true;
|
|
3919
|
+
}
|
|
3920
|
+
var writesTheKey = (key) => key !== null && stringLiteralValue(key) !== null;
|
|
3921
|
+
function firstArgumentOf(call) {
|
|
3922
|
+
const first = callArguments(call).find(
|
|
3923
|
+
(argument) => argument.kind === "positional" && argument.position === 0
|
|
3924
|
+
);
|
|
3925
|
+
return first?.node ?? null;
|
|
3926
|
+
}
|
|
3927
|
+
var sitesByDb = /* @__PURE__ */ new WeakMap();
|
|
3928
|
+
function bindEnvFacts(db, found) {
|
|
3929
|
+
const sites = found.flatMap((one) => one.sites);
|
|
3930
|
+
const objects = found.flatMap((one) => one.objects);
|
|
3931
|
+
if (sites.length === 0 && objects.length === 0) {
|
|
3932
|
+
return;
|
|
3933
|
+
}
|
|
3934
|
+
let index = sitesByDb.get(db);
|
|
3935
|
+
if (index === void 0) {
|
|
3936
|
+
index = { byId: /* @__PURE__ */ new Map(), objects: /* @__PURE__ */ new Set(), sitesByParameter: null };
|
|
3937
|
+
sitesByDb.set(db, index);
|
|
3938
|
+
}
|
|
3939
|
+
for (const object of objects) {
|
|
3940
|
+
db.add("environmentObject", [object]);
|
|
3941
|
+
index.objects.add(object);
|
|
3942
|
+
}
|
|
3943
|
+
for (const site of sites) {
|
|
3944
|
+
index.byId.set(site.site, site);
|
|
3945
|
+
if (site.functionKey === null) {
|
|
3946
|
+
continue;
|
|
3947
|
+
}
|
|
3948
|
+
db.add("environmentObject", [site.functionKey]);
|
|
3949
|
+
db.add("readsKeyed", [site.site, site.functionKey, site.nameKey]);
|
|
3950
|
+
index.objects.add(site.functionKey);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
function sitesEachParameterNames(db, index) {
|
|
3954
|
+
if (index.sitesByParameter !== null) {
|
|
3955
|
+
return index.sitesByParameter;
|
|
3956
|
+
}
|
|
3957
|
+
resolveEnvObjects(db, [...index.objects]);
|
|
3958
|
+
const found = /* @__PURE__ */ new Map();
|
|
3959
|
+
for (const row of db.facts("wantedParamNamesEnv")) {
|
|
3960
|
+
const parameter = String(row[0]);
|
|
3961
|
+
const named = found.get(parameter) ?? [];
|
|
3962
|
+
named.push(String(row[1]));
|
|
3963
|
+
found.set(parameter, named);
|
|
3964
|
+
}
|
|
3965
|
+
index.sitesByParameter = found;
|
|
3966
|
+
return found;
|
|
3967
|
+
}
|
|
3968
|
+
function helperReadsByCall(calls, db) {
|
|
3969
|
+
const found = /* @__PURE__ */ new Map();
|
|
3970
|
+
const index = db === void 0 ? void 0 : sitesByDb.get(db);
|
|
3971
|
+
if (db === void 0 || index === void 0 || calls.length === 0) {
|
|
3972
|
+
return found;
|
|
3973
|
+
}
|
|
3974
|
+
const named = sitesEachParameterNames(db, index);
|
|
3975
|
+
if (named.size === 0) {
|
|
3976
|
+
return found;
|
|
3977
|
+
}
|
|
3978
|
+
const calleeKeys = /* @__PURE__ */ new Map();
|
|
3979
|
+
for (const call of calls) {
|
|
3980
|
+
const callee = field(call, "function");
|
|
3981
|
+
const key = callee === null ? null : resolutionKeyOf(callee, db);
|
|
3982
|
+
if (key !== null) {
|
|
3983
|
+
calleeKeys.set(call.id, key);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
if (calleeKeys.size === 0) {
|
|
3987
|
+
return found;
|
|
3988
|
+
}
|
|
3989
|
+
resolveCalls(db, [...new Set(calleeKeys.values())]);
|
|
3990
|
+
for (const call of calls) {
|
|
3991
|
+
const key = calleeKeys.get(call.id);
|
|
3992
|
+
if (key === void 0) {
|
|
3993
|
+
continue;
|
|
3994
|
+
}
|
|
3995
|
+
const argumentAt2 = argumentsByParameter(db, call, key);
|
|
3996
|
+
if (argumentAt2.size === 0) {
|
|
3997
|
+
continue;
|
|
3998
|
+
}
|
|
3999
|
+
const reads = readsThroughHelper(db, call, argumentAt2, index.byId, named);
|
|
4000
|
+
if (reads.length > 0) {
|
|
4001
|
+
found.set(call.id, reads);
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
return found;
|
|
4005
|
+
}
|
|
4006
|
+
function argumentsByParameter(db, call, calleeKey) {
|
|
4007
|
+
const written = callArguments(call);
|
|
4008
|
+
if (written.length === 0) {
|
|
4009
|
+
return /* @__PURE__ */ new Map();
|
|
4010
|
+
}
|
|
4011
|
+
const positional = /* @__PURE__ */ new Map();
|
|
4012
|
+
const byName = /* @__PURE__ */ new Map();
|
|
4013
|
+
for (const argument of written) {
|
|
4014
|
+
if (argument.kind === "keyword") {
|
|
4015
|
+
byName.set(argument.name, argument.node);
|
|
4016
|
+
continue;
|
|
4017
|
+
}
|
|
4018
|
+
positional.set(String(argument.position), argument.node);
|
|
4019
|
+
}
|
|
4020
|
+
const argumentAt2 = /* @__PURE__ */ new Map();
|
|
4021
|
+
for (const callee of resolvedFunctions(db, calleeKey)) {
|
|
4022
|
+
for (const row of db.lookup("paramOf", 0, callee)) {
|
|
4023
|
+
const at = positional.get(String(row[1]));
|
|
4024
|
+
if (at !== void 0) {
|
|
4025
|
+
argumentAt2.set(String(row[2]), at);
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
for (const row of db.lookup("paramNamed", 0, callee)) {
|
|
4029
|
+
const at = byName.get(String(row[1]));
|
|
4030
|
+
if (at !== void 0) {
|
|
4031
|
+
argumentAt2.set(String(row[2]), at);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
return argumentAt2;
|
|
4036
|
+
}
|
|
4037
|
+
function readsThroughHelper(db, call, argumentAt2, siteById, named) {
|
|
4038
|
+
const reads = /* @__PURE__ */ new Map();
|
|
4039
|
+
for (const [parameter, argument] of argumentAt2) {
|
|
4040
|
+
const sites = named.get(parameter);
|
|
4041
|
+
if (sites === void 0) {
|
|
4042
|
+
continue;
|
|
4043
|
+
}
|
|
4044
|
+
const name = stringLiteralValue(argument) ?? stringValueOf(argument, db);
|
|
4045
|
+
if (name === null) {
|
|
4046
|
+
continue;
|
|
4047
|
+
}
|
|
4048
|
+
for (const id of sites) {
|
|
4049
|
+
const defaulted = siteById.get(id)?.defaulted ?? false;
|
|
4050
|
+
reads.set(name, (reads.get(name) ?? true) && defaulted);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
if (reads.size === 0) {
|
|
4054
|
+
return [];
|
|
4055
|
+
}
|
|
4056
|
+
const defaultedAtCall = isDefaultedAt(call);
|
|
4057
|
+
return [...reads].map(
|
|
4058
|
+
([name, defaulted]) => configReadEffect({ name, defaulted: defaulted || defaultedAtCall })
|
|
4059
|
+
);
|
|
4060
|
+
}
|
|
3693
4061
|
|
|
3694
4062
|
// src/paths/raisedResponses.ts
|
|
3695
4063
|
import { constantOf as constantOf3 } from "@suss/values";
|
|
@@ -3898,72 +4266,302 @@ function returnedBodyShape(statement) {
|
|
|
3898
4266
|
|
|
3899
4267
|
// src/rawSql.ts
|
|
3900
4268
|
import { storageBinding } from "@suss/ir-core";
|
|
3901
|
-
import { readSqlAccess, sqlFromParts } from "@suss/sql";
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4269
|
+
import { readSqlAccess, splitQualifiedTable, sqlFromParts } from "@suss/sql";
|
|
4270
|
+
import { force as force2 } from "@suss/values";
|
|
4271
|
+
|
|
4272
|
+
// src/receiverTypes.ts
|
|
4273
|
+
var UNDECLARED_MODULE = "builtins";
|
|
4274
|
+
function annotationTarget(annotation) {
|
|
4275
|
+
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
4276
|
+
return annotationTarget(annotation.namedChildren[0]);
|
|
3905
4277
|
}
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
4278
|
+
if (annotation.type === "binary_operator") {
|
|
4279
|
+
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4280
|
+
(side) => side !== null && side.type !== "none"
|
|
4281
|
+
);
|
|
4282
|
+
return named === void 0 || named === null ? null : annotationTarget(named);
|
|
4283
|
+
}
|
|
4284
|
+
if (annotation.type === "generic_type") {
|
|
4285
|
+
const outer = annotation.namedChildren[0];
|
|
4286
|
+
const first = genericTypeArgs(annotation)[0];
|
|
4287
|
+
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4288
|
+
return annotationTarget(first);
|
|
3911
4289
|
}
|
|
3912
|
-
|
|
3913
|
-
|
|
4290
|
+
return outer ?? null;
|
|
4291
|
+
}
|
|
4292
|
+
return annotation;
|
|
4293
|
+
}
|
|
4294
|
+
function typeNameOf(annotation) {
|
|
4295
|
+
const target = annotationTarget(annotation);
|
|
4296
|
+
if (target === null) {
|
|
4297
|
+
return null;
|
|
4298
|
+
}
|
|
4299
|
+
return target.type === "identifier" ? target.text : stringLiteralValue(target);
|
|
4300
|
+
}
|
|
4301
|
+
function firstInBody(node, read) {
|
|
4302
|
+
const here = read(node);
|
|
4303
|
+
if (here !== null) {
|
|
4304
|
+
return here;
|
|
4305
|
+
}
|
|
4306
|
+
for (const child of children(node)) {
|
|
4307
|
+
if (child.type === "function_definition" || child.type === "lambda") {
|
|
3914
4308
|
continue;
|
|
3915
4309
|
}
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
effects.push({
|
|
3920
|
-
type: "interaction",
|
|
3921
|
-
binding: storageBinding({
|
|
3922
|
-
recognition: `python-${pattern.module}`,
|
|
3923
|
-
storageSystem: pattern.storageSystem,
|
|
3924
|
-
scope: "default",
|
|
3925
|
-
container: access.table
|
|
3926
|
-
}),
|
|
3927
|
-
callee: field(call, "function")?.text ?? "",
|
|
3928
|
-
interaction: {
|
|
3929
|
-
class: "storage-access",
|
|
3930
|
-
kind: access.kind,
|
|
3931
|
-
fields: access.fields,
|
|
3932
|
-
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
3933
|
-
operation: field(call, "function")?.text ?? ""
|
|
3934
|
-
}
|
|
3935
|
-
});
|
|
4310
|
+
const found = firstInBody(child, read);
|
|
4311
|
+
if (found !== null) {
|
|
4312
|
+
return found;
|
|
3936
4313
|
}
|
|
3937
4314
|
}
|
|
3938
|
-
return
|
|
4315
|
+
return null;
|
|
3939
4316
|
}
|
|
3940
|
-
function
|
|
4317
|
+
function statedBy(statement, name) {
|
|
4318
|
+
if (statement.type === "assignment") {
|
|
4319
|
+
const annotation = field(statement, "type");
|
|
4320
|
+
return field(statement, "left")?.text === name ? annotation : null;
|
|
4321
|
+
}
|
|
4322
|
+
if (statement.type === "as_pattern") {
|
|
4323
|
+
const alias = field(statement, "alias");
|
|
4324
|
+
const value = statement.namedChildren[0] ?? null;
|
|
4325
|
+
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
4326
|
+
return alias?.text === name ? callee : null;
|
|
4327
|
+
}
|
|
4328
|
+
return null;
|
|
4329
|
+
}
|
|
4330
|
+
function statedTypeNode(name, from) {
|
|
4331
|
+
const fn = enclosingFunction(from);
|
|
4332
|
+
if (fn === null) {
|
|
4333
|
+
return null;
|
|
4334
|
+
}
|
|
4335
|
+
const params = field(fn, "parameters");
|
|
4336
|
+
for (const param of params === null ? [] : children(params)) {
|
|
4337
|
+
const info = parameterNameAndType(param);
|
|
4338
|
+
if (info?.name === name && info.typeNode !== null) {
|
|
4339
|
+
return info.typeNode;
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
const body = field(fn, "body");
|
|
4343
|
+
return body === null ? null : firstInBody(body, (statement) => statedBy(statement, name));
|
|
4344
|
+
}
|
|
4345
|
+
function statedTypeName(name, from) {
|
|
4346
|
+
const stated = statedTypeNode(name, from);
|
|
4347
|
+
return stated === null ? null : typeNameOf(stated);
|
|
4348
|
+
}
|
|
4349
|
+
function statedOrigins(stated, options) {
|
|
4350
|
+
const name = typeNameOf(stated);
|
|
4351
|
+
const byRule = name === null ? [] : originsOf(options.facts, `${options.filePath}#${name}`);
|
|
4352
|
+
const target = annotationTarget(stated);
|
|
4353
|
+
const imported = target === null ? null : originOf(target, moduleOf(target));
|
|
4354
|
+
if (imported === null || imported.module === UNDECLARED_MODULE) {
|
|
4355
|
+
return byRule;
|
|
4356
|
+
}
|
|
4357
|
+
return [...byRule, imported];
|
|
4358
|
+
}
|
|
4359
|
+
function receiverTypeOrigins(receiver, options) {
|
|
4360
|
+
const stated = statedTypeNode(receiver.text, receiver);
|
|
4361
|
+
if (stated !== null) {
|
|
4362
|
+
return statedOrigins(stated, options);
|
|
4363
|
+
}
|
|
4364
|
+
const built = constructionBehind(receiver, options.facts);
|
|
4365
|
+
if (built.type === "oneCall") {
|
|
4366
|
+
return [built.construction.origin];
|
|
4367
|
+
}
|
|
4368
|
+
const key = nameKeyIn(
|
|
4369
|
+
options.filePath,
|
|
4370
|
+
enclosingFunction(receiver),
|
|
4371
|
+
receiver.text
|
|
4372
|
+
);
|
|
4373
|
+
return subjectConstructions(options.facts, [key]).get(key)?.origins ?? [];
|
|
4374
|
+
}
|
|
4375
|
+
|
|
4376
|
+
// src/rawSql.ts
|
|
4377
|
+
function rawSqlEffects(calls, options) {
|
|
4378
|
+
return matchesIn(calls, options).flatMap(
|
|
4379
|
+
(match) => match.accesses.flatMap((access) => effectFor(match, access))
|
|
4380
|
+
);
|
|
4381
|
+
}
|
|
4382
|
+
function rawSqlCallIds(calls, options) {
|
|
4383
|
+
return new Set(matchesIn(calls, options).map((match) => match.call.id));
|
|
4384
|
+
}
|
|
4385
|
+
function matchesIn(calls, options) {
|
|
4386
|
+
if (options.patterns.length === 0 && (options.clients ?? []).length === 0) {
|
|
4387
|
+
return [];
|
|
4388
|
+
}
|
|
4389
|
+
const matched = [];
|
|
4390
|
+
for (const call of calls) {
|
|
4391
|
+
const one = importedFunctionMatch(call, options) ?? clientMatch(call, options);
|
|
4392
|
+
if (one !== null) {
|
|
4393
|
+
matched.push(one);
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
return matched;
|
|
4397
|
+
}
|
|
4398
|
+
function effectFor(match, access) {
|
|
4399
|
+
const operation = field(match.call, "function")?.text ?? "";
|
|
4400
|
+
return [
|
|
4401
|
+
{
|
|
4402
|
+
type: "interaction",
|
|
4403
|
+
binding: storageBinding({
|
|
4404
|
+
recognition: match.recognition,
|
|
4405
|
+
storageSystem: match.storageSystem,
|
|
4406
|
+
scope: access.qualifier[access.qualifier.length - 1] ?? NO_GROUP,
|
|
4407
|
+
container: access.table
|
|
4408
|
+
}),
|
|
4409
|
+
callee: operation,
|
|
4410
|
+
interaction: {
|
|
4411
|
+
class: "storage-access",
|
|
4412
|
+
kind: access.kind,
|
|
4413
|
+
fields: access.fields,
|
|
4414
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
4415
|
+
operation
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
];
|
|
4419
|
+
}
|
|
4420
|
+
var NO_GROUP = "default";
|
|
4421
|
+
function importedFunctionMatch(call, options) {
|
|
3941
4422
|
const callee = field(call, "function");
|
|
3942
4423
|
if (callee === null || callee.type !== "identifier") {
|
|
3943
|
-
return
|
|
4424
|
+
return null;
|
|
3944
4425
|
}
|
|
3945
4426
|
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
3946
4427
|
if (from === void 0) {
|
|
3947
|
-
return
|
|
4428
|
+
return null;
|
|
3948
4429
|
}
|
|
3949
4430
|
const module = String(from[1]);
|
|
3950
|
-
|
|
3951
|
-
(
|
|
4431
|
+
const pattern = options.patterns.find(
|
|
4432
|
+
(candidate) => candidate.module === module && candidate.functions.includes(callee.text)
|
|
3952
4433
|
);
|
|
3953
|
-
|
|
3954
|
-
|
|
4434
|
+
if (pattern === void 0) {
|
|
4435
|
+
return null;
|
|
4436
|
+
}
|
|
3955
4437
|
const args = field(call, "arguments");
|
|
3956
4438
|
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
3957
|
-
|
|
4439
|
+
const statement = first === null ? null : statementAt(first, void 0, options.facts);
|
|
4440
|
+
if (statement === null) {
|
|
3958
4441
|
return null;
|
|
3959
4442
|
}
|
|
3960
|
-
|
|
4443
|
+
return {
|
|
4444
|
+
call,
|
|
4445
|
+
recognition: `python-${pattern.module}`,
|
|
4446
|
+
storageSystem: pattern.storageSystem,
|
|
4447
|
+
accesses: readSqlAccess(statement, { dialect: pattern.storageSystem })
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4450
|
+
function clientMatch(call, options) {
|
|
4451
|
+
const callee = field(call, "function");
|
|
4452
|
+
if (callee === null || callee.type !== "attribute") {
|
|
4453
|
+
return null;
|
|
4454
|
+
}
|
|
4455
|
+
const method = field(callee, "attribute")?.text ?? "";
|
|
4456
|
+
const receiver = field(callee, "object");
|
|
4457
|
+
if (receiver === null) {
|
|
4458
|
+
return null;
|
|
4459
|
+
}
|
|
4460
|
+
for (const pattern of options.clients ?? []) {
|
|
4461
|
+
const accesses = accessesOf(call, method, pattern, options);
|
|
4462
|
+
if (accesses === null || !isClientOf(receiver, pattern, options)) {
|
|
4463
|
+
continue;
|
|
4464
|
+
}
|
|
4465
|
+
return {
|
|
4466
|
+
call,
|
|
4467
|
+
recognition: `python-${pattern.module}`,
|
|
4468
|
+
storageSystem: pattern.storageSystem,
|
|
4469
|
+
accesses
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
return null;
|
|
4473
|
+
}
|
|
4474
|
+
function accessesOf(call, method, pattern, options) {
|
|
4475
|
+
const dialect = pattern.dialect ?? pattern.storageSystem;
|
|
4476
|
+
for (const takes of pattern.statements ?? []) {
|
|
4477
|
+
if (takes.method !== method) {
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
const written = argumentNode(call, takes);
|
|
4481
|
+
const statement = written === null ? null : statementAt(written, takes.path, options.facts);
|
|
4482
|
+
return statement === null ? [] : readSqlAccess(statement, { dialect });
|
|
4483
|
+
}
|
|
4484
|
+
for (const takes of pattern.tables ?? []) {
|
|
4485
|
+
if (takes.method !== method) {
|
|
4486
|
+
continue;
|
|
4487
|
+
}
|
|
4488
|
+
const written = argumentNode(call, takes);
|
|
4489
|
+
const name = written === null ? null : stringValueOf(written, options.facts);
|
|
4490
|
+
const split = name === null ? null : splitQualifiedTable(name);
|
|
4491
|
+
return split === null ? [] : [{ ...split, kind: takes.kind, fields: [], selector: [] }];
|
|
4492
|
+
}
|
|
4493
|
+
return null;
|
|
4494
|
+
}
|
|
4495
|
+
function isClientOf(receiver, pattern, options) {
|
|
4496
|
+
return receiverOrigins(receiver, options).some(
|
|
4497
|
+
(origin) => origin.module === pattern.module && pattern.clientTypes.includes(origin.name)
|
|
4498
|
+
);
|
|
4499
|
+
}
|
|
4500
|
+
function receiverOrigins(receiver, options) {
|
|
4501
|
+
if (receiver.type === "identifier") {
|
|
4502
|
+
return receiverTypeOrigins(receiver, options);
|
|
4503
|
+
}
|
|
4504
|
+
return receiver.type === "call" ? handedBack(receiver, options) : [];
|
|
4505
|
+
}
|
|
4506
|
+
function handedBack(call, options) {
|
|
4507
|
+
const callee = field(call, "function");
|
|
4508
|
+
if (callee === null || callee.type !== "attribute") {
|
|
4509
|
+
return [];
|
|
4510
|
+
}
|
|
4511
|
+
const method = field(callee, "attribute")?.text ?? "";
|
|
4512
|
+
const inner = field(callee, "object");
|
|
4513
|
+
if (inner === null) {
|
|
4514
|
+
return [];
|
|
4515
|
+
}
|
|
4516
|
+
const found = [];
|
|
4517
|
+
for (const pattern of options.clients ?? []) {
|
|
4518
|
+
const handoff = (pattern.handsBack ?? []).find(
|
|
4519
|
+
(one) => one.method === method
|
|
4520
|
+
);
|
|
4521
|
+
if (handoff !== void 0 && isClientOf(inner, pattern, options)) {
|
|
4522
|
+
found.push({ module: handoff.module, name: handoff.name });
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
return found;
|
|
4526
|
+
}
|
|
4527
|
+
function argumentNode(call, at) {
|
|
4528
|
+
const args = field(call, "arguments");
|
|
4529
|
+
const written = args === null ? [] : children(args);
|
|
4530
|
+
const named = written.find(
|
|
4531
|
+
(one) => one.type === "keyword_argument" && at.keyword !== void 0 && field(one, "name")?.text === at.keyword
|
|
4532
|
+
);
|
|
4533
|
+
const value = named === void 0 ? null : field(named, "value");
|
|
4534
|
+
if (value !== null) {
|
|
4535
|
+
return value;
|
|
4536
|
+
}
|
|
4537
|
+
if (at.argument === void 0) {
|
|
4538
|
+
return null;
|
|
4539
|
+
}
|
|
4540
|
+
return written.filter((one) => one.type !== "keyword_argument")[at.argument] ?? null;
|
|
4541
|
+
}
|
|
4542
|
+
function statementAt(node, path6, facts) {
|
|
4543
|
+
const reached = valueAt(evaluatedValue(node, facts), path6 ?? []);
|
|
4544
|
+
const parts = reached === null ? null : literalParts(reached);
|
|
3961
4545
|
if (parts === null) {
|
|
3962
4546
|
return null;
|
|
3963
4547
|
}
|
|
3964
4548
|
const statement = sqlFromParts(parts);
|
|
3965
4549
|
return statement.trim() === "" ? null : statement;
|
|
3966
4550
|
}
|
|
4551
|
+
function valueAt(value, path6) {
|
|
4552
|
+
let reached = value;
|
|
4553
|
+
for (const key of path6) {
|
|
4554
|
+
if (reached.kind !== "record") {
|
|
4555
|
+
return null;
|
|
4556
|
+
}
|
|
4557
|
+
const item = reached.fields.get(key);
|
|
4558
|
+
if (item === void 0) {
|
|
4559
|
+
return null;
|
|
4560
|
+
}
|
|
4561
|
+
reached = force2(item.value);
|
|
4562
|
+
}
|
|
4563
|
+
return reached;
|
|
4564
|
+
}
|
|
3967
4565
|
function literalParts(value) {
|
|
3968
4566
|
if (value.kind !== "string") {
|
|
3969
4567
|
return null;
|
|
@@ -4061,78 +4659,7 @@ function receiverName(chain) {
|
|
|
4061
4659
|
return null;
|
|
4062
4660
|
}
|
|
4063
4661
|
const object = field(callee, "object");
|
|
4064
|
-
return object !== null && object.type === "identifier" ? object
|
|
4065
|
-
}
|
|
4066
|
-
function typeNameOf(annotation) {
|
|
4067
|
-
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
4068
|
-
return typeNameOf(annotation.namedChildren[0]);
|
|
4069
|
-
}
|
|
4070
|
-
if (annotation.type === "identifier") {
|
|
4071
|
-
return annotation.text;
|
|
4072
|
-
}
|
|
4073
|
-
const quoted = stringLiteralValue(annotation);
|
|
4074
|
-
if (quoted !== null) {
|
|
4075
|
-
return quoted;
|
|
4076
|
-
}
|
|
4077
|
-
if (annotation.type === "binary_operator") {
|
|
4078
|
-
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4079
|
-
(side) => side !== null && side.type !== "none"
|
|
4080
|
-
);
|
|
4081
|
-
return named === void 0 || named === null ? null : typeNameOf(named);
|
|
4082
|
-
}
|
|
4083
|
-
if (annotation.type === "generic_type") {
|
|
4084
|
-
const outer = annotation.namedChildren[0];
|
|
4085
|
-
const first = genericTypeArgs(annotation)[0];
|
|
4086
|
-
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4087
|
-
return typeNameOf(first);
|
|
4088
|
-
}
|
|
4089
|
-
return outer?.type === "identifier" ? outer.text : null;
|
|
4090
|
-
}
|
|
4091
|
-
return null;
|
|
4092
|
-
}
|
|
4093
|
-
function statedTypeName(name, from) {
|
|
4094
|
-
const fn = enclosingFunction(from);
|
|
4095
|
-
if (fn === null) {
|
|
4096
|
-
return null;
|
|
4097
|
-
}
|
|
4098
|
-
const params = field(fn, "parameters");
|
|
4099
|
-
for (const param of params === null ? [] : children(params)) {
|
|
4100
|
-
const info = parameterNameAndType(param);
|
|
4101
|
-
if (info?.name === name && info.typeNode !== null) {
|
|
4102
|
-
return typeNameOf(info.typeNode);
|
|
4103
|
-
}
|
|
4104
|
-
}
|
|
4105
|
-
const body = field(fn, "body");
|
|
4106
|
-
return body === null ? null : firstInBody(body, (statement) => statedBy(statement, name));
|
|
4107
|
-
}
|
|
4108
|
-
function firstInBody(node, read) {
|
|
4109
|
-
const here = read(node);
|
|
4110
|
-
if (here !== null) {
|
|
4111
|
-
return here;
|
|
4112
|
-
}
|
|
4113
|
-
for (const child of children(node)) {
|
|
4114
|
-
if (child.type === "function_definition" || child.type === "lambda") {
|
|
4115
|
-
continue;
|
|
4116
|
-
}
|
|
4117
|
-
const found = firstInBody(child, read);
|
|
4118
|
-
if (found !== null) {
|
|
4119
|
-
return found;
|
|
4120
|
-
}
|
|
4121
|
-
}
|
|
4122
|
-
return null;
|
|
4123
|
-
}
|
|
4124
|
-
function statedBy(statement, name) {
|
|
4125
|
-
if (statement.type === "assignment") {
|
|
4126
|
-
const annotation = field(statement, "type");
|
|
4127
|
-
return field(statement, "left")?.text === name && annotation !== null ? typeNameOf(annotation) : null;
|
|
4128
|
-
}
|
|
4129
|
-
if (statement.type === "as_pattern") {
|
|
4130
|
-
const alias = field(statement, "alias");
|
|
4131
|
-
const value = statement.namedChildren[0] ?? null;
|
|
4132
|
-
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
4133
|
-
return alias?.text === name && callee?.type === "identifier" ? callee.text : null;
|
|
4134
|
-
}
|
|
4135
|
-
return null;
|
|
4662
|
+
return object !== null && object.type === "identifier" ? object : null;
|
|
4136
4663
|
}
|
|
4137
4664
|
function builtType(call, options) {
|
|
4138
4665
|
const root = rootOf(call);
|
|
@@ -4180,7 +4707,7 @@ function typedReceiverPattern(options, chain) {
|
|
|
4180
4707
|
if (receiver === null) {
|
|
4181
4708
|
return void 0;
|
|
4182
4709
|
}
|
|
4183
|
-
for (const origin of receiverTypeOrigins(
|
|
4710
|
+
for (const origin of receiverTypeOrigins(receiver, options)) {
|
|
4184
4711
|
const pattern = queryTypePattern(options, origin.module, origin.name);
|
|
4185
4712
|
if (pattern !== void 0) {
|
|
4186
4713
|
return pattern;
|
|
@@ -4188,14 +4715,6 @@ function typedReceiverPattern(options, chain) {
|
|
|
4188
4715
|
}
|
|
4189
4716
|
return void 0;
|
|
4190
4717
|
}
|
|
4191
|
-
function receiverTypeOrigins(options, name, from) {
|
|
4192
|
-
const stated = statedTypeName(name, from);
|
|
4193
|
-
if (stated !== null) {
|
|
4194
|
-
return originsOf(options.facts, `${options.filePath}#${stated}`);
|
|
4195
|
-
}
|
|
4196
|
-
const key = nameKeyIn(options.filePath, enclosingFunction(from), name);
|
|
4197
|
-
return subjectConstructions(options.facts, [key]).get(key)?.origins ?? [];
|
|
4198
|
-
}
|
|
4199
4718
|
function queryTypePattern(options, module, name) {
|
|
4200
4719
|
return options.patterns.find(
|
|
4201
4720
|
(pattern) => module === pattern.module && pattern.queryTypes.includes(name)
|
|
@@ -4317,7 +4836,7 @@ function classBehind(typeName, options) {
|
|
|
4317
4836
|
function isClassName(name) {
|
|
4318
4837
|
return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
|
|
4319
4838
|
}
|
|
4320
|
-
function
|
|
4839
|
+
function effectFor2(pattern, chain, operation, options) {
|
|
4321
4840
|
const valueMethods = pattern.valueMethods ?? [];
|
|
4322
4841
|
const picked = selectorOf(chain, valueMethods);
|
|
4323
4842
|
return {
|
|
@@ -4427,7 +4946,7 @@ function storageCallIds(calls, options) {
|
|
|
4427
4946
|
}
|
|
4428
4947
|
function storageEffects(calls, options) {
|
|
4429
4948
|
return matchedChains(calls, options).flatMap(
|
|
4430
|
-
({ chain, pattern, operation }) => operation === null ? [] : [
|
|
4949
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor2(pattern, chain, operation, options)]
|
|
4431
4950
|
);
|
|
4432
4951
|
}
|
|
4433
4952
|
|
|
@@ -4462,8 +4981,10 @@ function decoratedStatements(root, module, facts) {
|
|
|
4462
4981
|
}));
|
|
4463
4982
|
}
|
|
4464
4983
|
function discoverUnits(root, module, options) {
|
|
4984
|
+
const decorated = decoratedStatements(root, module, options.facts);
|
|
4985
|
+
askWrittenValues(clientCallReceivers(root, module), options.facts);
|
|
4465
4986
|
return [
|
|
4466
|
-
...
|
|
4987
|
+
...decorated.flatMap(
|
|
4467
4988
|
(decoratedStatement) => decoratedUnits(decoratedStatement, module, options)
|
|
4468
4989
|
),
|
|
4469
4990
|
...clientUnits(root, module, options)
|
|
@@ -5153,7 +5674,12 @@ function buildRouteUnit(options) {
|
|
|
5153
5674
|
() => readReturnAnnotation(definitionNode, enclosingScope, ctx)
|
|
5154
5675
|
);
|
|
5155
5676
|
const effects = invocationEffects(definitionNode, options.facts);
|
|
5156
|
-
const extra = recognizedBodyEffects(
|
|
5677
|
+
const extra = recognizedBodyEffects(
|
|
5678
|
+
definitionNode,
|
|
5679
|
+
module,
|
|
5680
|
+
storageLookup,
|
|
5681
|
+
options.facts
|
|
5682
|
+
);
|
|
5157
5683
|
const perTerminal = branchesPerTerminal({
|
|
5158
5684
|
definitionNode,
|
|
5159
5685
|
effects,
|
|
@@ -5238,28 +5764,35 @@ function unreadRoleReadings(parameters, range) {
|
|
|
5238
5764
|
)
|
|
5239
5765
|
];
|
|
5240
5766
|
}
|
|
5241
|
-
function recognizedBodyEffects(definitionNode, module, storageLookup) {
|
|
5767
|
+
function recognizedBodyEffects(definitionNode, module, storageLookup, facts) {
|
|
5242
5768
|
const storage = storageLookup === void 0 ? [] : [
|
|
5243
5769
|
...storageEffects(bodyCalls(definitionNode), {
|
|
5244
5770
|
...storageLookup,
|
|
5245
5771
|
filePath: storageLookup.factsPath
|
|
5246
5772
|
}),
|
|
5247
|
-
...rawSqlEffects(bodyCalls(definitionNode),
|
|
5248
|
-
facts: storageLookup.facts,
|
|
5249
|
-
filePath: storageLookup.factsPath,
|
|
5250
|
-
patterns: storageLookup.rawSql ?? []
|
|
5251
|
-
})
|
|
5773
|
+
...rawSqlEffects(bodyCalls(definitionNode), rawSqlOf(storageLookup))
|
|
5252
5774
|
];
|
|
5253
|
-
return [...envReadEffects(definitionNode, module), ...storage];
|
|
5775
|
+
return [...envReadEffects(definitionNode, module, facts), ...storage];
|
|
5776
|
+
}
|
|
5777
|
+
function rawSqlOf(storageLookup) {
|
|
5778
|
+
return {
|
|
5779
|
+
facts: storageLookup.facts,
|
|
5780
|
+
filePath: storageLookup.factsPath,
|
|
5781
|
+
patterns: storageLookup.rawSql ?? [],
|
|
5782
|
+
clients: storageLookup.sqlClients ?? []
|
|
5783
|
+
};
|
|
5254
5784
|
}
|
|
5255
|
-
function recognizedCallIds(
|
|
5785
|
+
function recognizedCallIds(calls, storageLookup) {
|
|
5256
5786
|
if (storageLookup === void 0) {
|
|
5257
5787
|
return /* @__PURE__ */ new Set();
|
|
5258
5788
|
}
|
|
5259
|
-
return
|
|
5260
|
-
...
|
|
5261
|
-
|
|
5262
|
-
|
|
5789
|
+
return /* @__PURE__ */ new Set([
|
|
5790
|
+
...storageCallIds(calls, {
|
|
5791
|
+
...storageLookup,
|
|
5792
|
+
filePath: storageLookup.factsPath
|
|
5793
|
+
}),
|
|
5794
|
+
...rawSqlCallIds(calls, rawSqlOf(storageLookup))
|
|
5795
|
+
]);
|
|
5263
5796
|
}
|
|
5264
5797
|
function bodyContentOf(bodyNode) {
|
|
5265
5798
|
for (const stmt of bodyStatements(bodyNode)) {
|
|
@@ -5599,6 +6132,7 @@ import {
|
|
|
5599
6132
|
composeWrappers,
|
|
5600
6133
|
createCacheLayer,
|
|
5601
6134
|
createTimer,
|
|
6135
|
+
effectToIR,
|
|
5602
6136
|
moduleInitStructure,
|
|
5603
6137
|
noopTimer,
|
|
5604
6138
|
runDigest,
|
|
@@ -5731,11 +6265,14 @@ function resolveCallee2(call, site, ctx, read) {
|
|
|
5731
6265
|
const outcome = read?.outcomes.get(spelling.key) ?? calleeOutcomeOf(ctx.facts, spelling.key);
|
|
5732
6266
|
return asCallee(outcome, site.owner, ctx);
|
|
5733
6267
|
}
|
|
5734
|
-
function
|
|
6268
|
+
function namedOutcomes(keys, ctx) {
|
|
6269
|
+
return calleeOutcomes(ctx.facts, [...new Set(keys)]);
|
|
6270
|
+
}
|
|
6271
|
+
function functionNamed(nameKey2, ctx, read) {
|
|
5735
6272
|
if (assignedElsewhere(ctx.facts, nameKey2)) {
|
|
5736
6273
|
return null;
|
|
5737
6274
|
}
|
|
5738
|
-
const outcome = calleeOutcomeOf(ctx.facts, nameKey2);
|
|
6275
|
+
const outcome = read?.get(nameKey2) ?? calleeOutcomeOf(ctx.facts, nameKey2);
|
|
5739
6276
|
return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
|
|
5740
6277
|
}
|
|
5741
6278
|
function assignedElsewhere(facts, nameKey2) {
|
|
@@ -5943,6 +6480,7 @@ function reachedFunctions(seeds, options) {
|
|
|
5943
6480
|
const stopsByKey = /* @__PURE__ */ new Map();
|
|
5944
6481
|
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
5945
6482
|
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
6483
|
+
const callsByKey = /* @__PURE__ */ new Map();
|
|
5946
6484
|
const passedPositions = /* @__PURE__ */ new Set();
|
|
5947
6485
|
for (const seed of seeds) {
|
|
5948
6486
|
seedKeys.add(seed.key);
|
|
@@ -5969,12 +6507,21 @@ function reachedFunctions(seeds, options) {
|
|
|
5969
6507
|
bodies.flatMap((body) => body.written),
|
|
5970
6508
|
ctx
|
|
5971
6509
|
);
|
|
6510
|
+
const passed = namedOutcomes(
|
|
6511
|
+
bodies.flatMap(
|
|
6512
|
+
({ source, written }) => written.flatMap(({ call }) => passedNameKeys(source.file, call))
|
|
6513
|
+
),
|
|
6514
|
+
ctx
|
|
6515
|
+
);
|
|
5972
6516
|
for (const { key, source, body, written } of bodies) {
|
|
5973
6517
|
const scan = scanBody(
|
|
5974
6518
|
source,
|
|
5975
6519
|
ctx,
|
|
5976
|
-
recognizedCallIds(
|
|
5977
|
-
|
|
6520
|
+
recognizedCallIds(
|
|
6521
|
+
written.map(({ call }) => call),
|
|
6522
|
+
options.storageFor(source.file)
|
|
6523
|
+
),
|
|
6524
|
+
{ body, written, spellings, passed }
|
|
5978
6525
|
);
|
|
5979
6526
|
if (scan.stops.length > 0) {
|
|
5980
6527
|
stopsByKey.set(key, scan.stops);
|
|
@@ -5987,21 +6534,27 @@ function reachedFunctions(seeds, options) {
|
|
|
5987
6534
|
for (const position of scan.passedPositions) {
|
|
5988
6535
|
passedPositions.add(position);
|
|
5989
6536
|
}
|
|
6537
|
+
const called = [];
|
|
5990
6538
|
for (const target of scan.followed) {
|
|
5991
|
-
const calleeKey =
|
|
6539
|
+
const calleeKey = keyOf2(target);
|
|
5992
6540
|
if (!functionByKey.has(calleeKey)) {
|
|
5993
6541
|
functionByKey.set(calleeKey, target);
|
|
5994
6542
|
}
|
|
5995
6543
|
db.add("calls", [key, calleeKey]);
|
|
6544
|
+
called.push(calleeKey);
|
|
6545
|
+
}
|
|
6546
|
+
if (called.length > 0) {
|
|
6547
|
+
callsByKey.set(key, called);
|
|
5996
6548
|
}
|
|
5997
6549
|
}
|
|
5998
6550
|
}
|
|
6551
|
+
const reached = db.facts("reachable").map(([keyAtom]) => String(keyAtom)).filter((key) => !seedKeys.has(key));
|
|
6552
|
+
settleBodyValues(reached, functionByKey, options.facts);
|
|
5999
6553
|
const summaries = [];
|
|
6000
6554
|
const summariesByKey = /* @__PURE__ */ new Map();
|
|
6001
|
-
for (const
|
|
6002
|
-
const key = String(keyAtom);
|
|
6555
|
+
for (const key of reached) {
|
|
6003
6556
|
const target = functionByKey.get(key);
|
|
6004
|
-
if (
|
|
6557
|
+
if (target === void 0) {
|
|
6005
6558
|
continue;
|
|
6006
6559
|
}
|
|
6007
6560
|
const summary = assembleSummary(libraryUnit(target, options), {
|
|
@@ -6026,10 +6579,11 @@ function reachedFunctions(seeds, options) {
|
|
|
6026
6579
|
stopsByKey,
|
|
6027
6580
|
argTargetsByKey,
|
|
6028
6581
|
parameterCallsByKey,
|
|
6582
|
+
callsByKey,
|
|
6029
6583
|
passedPositions
|
|
6030
6584
|
};
|
|
6031
6585
|
}
|
|
6032
|
-
function
|
|
6586
|
+
function keyOf2(target) {
|
|
6033
6587
|
return nodeId(target.file.file, target.node);
|
|
6034
6588
|
}
|
|
6035
6589
|
var EMPTY_SCAN = {
|
|
@@ -6042,15 +6596,53 @@ var EMPTY_SCAN = {
|
|
|
6042
6596
|
};
|
|
6043
6597
|
function bodyOf(source) {
|
|
6044
6598
|
const { file, node } = source;
|
|
6045
|
-
const body = field(node, "body");
|
|
6599
|
+
const body = isModule(node) ? node : field(node, "body");
|
|
6046
6600
|
if (body === null) {
|
|
6047
6601
|
return { body, written: [] };
|
|
6048
6602
|
}
|
|
6049
6603
|
return {
|
|
6050
6604
|
body,
|
|
6051
|
-
written: callsWritten(file, body, scopeAt2(file, node),
|
|
6605
|
+
written: callsWritten(file, body, scopeAt2(file, node), keyOf2(source), {
|
|
6606
|
+
descendsInto: isModule(node) ? runsAtModuleLoad : outsideNestedDef
|
|
6607
|
+
})
|
|
6052
6608
|
};
|
|
6053
6609
|
}
|
|
6610
|
+
function outsideNestedDef(node) {
|
|
6611
|
+
return node.type !== "function_definition";
|
|
6612
|
+
}
|
|
6613
|
+
function settleBodyValues(reached, functionByKey, facts) {
|
|
6614
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
6615
|
+
for (const key of reached) {
|
|
6616
|
+
const target = functionByKey.get(key);
|
|
6617
|
+
if (target === void 0) {
|
|
6618
|
+
continue;
|
|
6619
|
+
}
|
|
6620
|
+
const listed = byFile.get(target.file.file) ?? [];
|
|
6621
|
+
for (const node of bodyValueNodes(target.node)) {
|
|
6622
|
+
listed.push(node);
|
|
6623
|
+
}
|
|
6624
|
+
byFile.set(target.file.file, listed);
|
|
6625
|
+
}
|
|
6626
|
+
for (const nodes of byFile.values()) {
|
|
6627
|
+
askWrittenValues(nodes, facts);
|
|
6628
|
+
}
|
|
6629
|
+
}
|
|
6630
|
+
function passedNameKeyOf(file, arg) {
|
|
6631
|
+
if (arg === null || arg.type !== "identifier") {
|
|
6632
|
+
return null;
|
|
6633
|
+
}
|
|
6634
|
+
return readKey(file.file, arg, enclosingFunction(arg));
|
|
6635
|
+
}
|
|
6636
|
+
function passedNameKeys(file, call) {
|
|
6637
|
+
const args = field(call, "arguments");
|
|
6638
|
+
if (args === null) {
|
|
6639
|
+
return [];
|
|
6640
|
+
}
|
|
6641
|
+
return args.namedChildren.flatMap((arg) => {
|
|
6642
|
+
const key = passedNameKeyOf(file, arg);
|
|
6643
|
+
return key === null ? [] : [key];
|
|
6644
|
+
});
|
|
6645
|
+
}
|
|
6054
6646
|
function scanBody(source, ctx, recognized, read) {
|
|
6055
6647
|
const followed = [];
|
|
6056
6648
|
const stops = [];
|
|
@@ -6071,17 +6663,15 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6071
6663
|
return;
|
|
6072
6664
|
}
|
|
6073
6665
|
args.namedChildren.forEach((arg, position) => {
|
|
6074
|
-
|
|
6666
|
+
const nameKey2 = passedNameKeyOf(file, arg);
|
|
6667
|
+
if (nameKey2 === null) {
|
|
6075
6668
|
return;
|
|
6076
6669
|
}
|
|
6077
|
-
const resolved = functionNamed(
|
|
6078
|
-
readKey(file.file, arg, enclosingFunction(arg)),
|
|
6079
|
-
ctx
|
|
6080
|
-
);
|
|
6670
|
+
const resolved = functionNamed(nameKey2, ctx, read.passed);
|
|
6081
6671
|
if (resolved === null) {
|
|
6082
6672
|
return;
|
|
6083
6673
|
}
|
|
6084
|
-
const key =
|
|
6674
|
+
const key = keyOf2(resolved);
|
|
6085
6675
|
if (!seen.has(key)) {
|
|
6086
6676
|
seen.add(key);
|
|
6087
6677
|
followed.push(resolved);
|
|
@@ -6106,7 +6696,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6106
6696
|
recordPassedArgs(
|
|
6107
6697
|
call,
|
|
6108
6698
|
callee,
|
|
6109
|
-
outcome.kind === "followed" ?
|
|
6699
|
+
outcome.kind === "followed" ? keyOf2(outcome.target) : null
|
|
6110
6700
|
);
|
|
6111
6701
|
if (outcome.kind === "stopped") {
|
|
6112
6702
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
@@ -6123,7 +6713,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6123
6713
|
}
|
|
6124
6714
|
return;
|
|
6125
6715
|
}
|
|
6126
|
-
const key =
|
|
6716
|
+
const key = keyOf2(outcome.target);
|
|
6127
6717
|
if (!seen.has(key)) {
|
|
6128
6718
|
seen.add(key);
|
|
6129
6719
|
followed.push(outcome.target);
|
|
@@ -6141,7 +6731,7 @@ function scanBody(source, ctx, recognized, read) {
|
|
|
6141
6731
|
passedPositions
|
|
6142
6732
|
};
|
|
6143
6733
|
}
|
|
6144
|
-
function callsWritten(file, body, outer, owner) {
|
|
6734
|
+
function callsWritten(file, body, outer, owner, limit) {
|
|
6145
6735
|
const found = [];
|
|
6146
6736
|
walkDescendants3(body, outer, {
|
|
6147
6737
|
at: (child, scope) => {
|
|
@@ -6149,7 +6739,7 @@ function callsWritten(file, body, outer, owner) {
|
|
|
6149
6739
|
found.push({ call: child, site: { file, scope, owner } });
|
|
6150
6740
|
}
|
|
6151
6741
|
},
|
|
6152
|
-
into: (child, scope) => child
|
|
6742
|
+
into: (child, scope) => limit.descendsInto(child) ? file.module.scopeFor.get(child.id) ?? scope : SKIP_CHILDREN2
|
|
6153
6743
|
});
|
|
6154
6744
|
return found;
|
|
6155
6745
|
}
|
|
@@ -6182,7 +6772,8 @@ function libraryUnit(target, options) {
|
|
|
6182
6772
|
const extra = recognizedBodyEffects(
|
|
6183
6773
|
node,
|
|
6184
6774
|
file.module,
|
|
6185
|
-
options.storageFor(file)
|
|
6775
|
+
options.storageFor(file),
|
|
6776
|
+
options.facts
|
|
6186
6777
|
);
|
|
6187
6778
|
const range = rangeOf(node);
|
|
6188
6779
|
return {
|
|
@@ -7763,7 +8354,8 @@ function wrapperUnit(target, declared, options) {
|
|
|
7763
8354
|
const extra = recognizedBodyEffects(
|
|
7764
8355
|
node,
|
|
7765
8356
|
file.module,
|
|
7766
|
-
options.storageFor(file)
|
|
8357
|
+
options.storageFor(file),
|
|
8358
|
+
options.facts
|
|
7767
8359
|
);
|
|
7768
8360
|
const delegate = (at) => ({
|
|
7769
8361
|
terminal: {
|
|
@@ -7921,6 +8513,7 @@ function factsForFile(options) {
|
|
|
7921
8513
|
files: [{ file: options.file, root: options.root, module: options.module }],
|
|
7922
8514
|
definitions
|
|
7923
8515
|
});
|
|
8516
|
+
bindEnvFacts(db, [envFactsIn(options.file, options.root, options.module)]);
|
|
7924
8517
|
addPackWords(db, packWordsOf(options.packs));
|
|
7925
8518
|
return db;
|
|
7926
8519
|
}
|
|
@@ -7971,6 +8564,7 @@ async function extractPythonProject(options) {
|
|
|
7971
8564
|
);
|
|
7972
8565
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
7973
8566
|
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
8567
|
+
const sqlClients = options.packs.flatMap((pack) => pack.sqlClients ?? []);
|
|
7974
8568
|
const modelQueries = options.packs.flatMap((pack) => pack.models ?? []);
|
|
7975
8569
|
const discovers = options.packs.some((pack) => pack.discovery.length > 0);
|
|
7976
8570
|
const buildsReceivers = options.packs.some(
|
|
@@ -7978,7 +8572,15 @@ async function extractPythonProject(options) {
|
|
|
7978
8572
|
(client) => (client.receiverConstructors ?? []).length > 0
|
|
7979
8573
|
)
|
|
7980
8574
|
);
|
|
7981
|
-
const
|
|
8575
|
+
const envFacts = bound.map(
|
|
8576
|
+
(boundFile) => envFactsIn(boundFile.file, boundFile.root, boundFile.module)
|
|
8577
|
+
);
|
|
8578
|
+
const readsEnvThroughNames = envFacts.some(
|
|
8579
|
+
(one) => one.sites.length > 0 || one.objects.length > 0
|
|
8580
|
+
);
|
|
8581
|
+
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
|
|
8582
|
+
// call that hands it over is read off.
|
|
8583
|
+
rawSqlPatterns.length > 0 || sqlClients.length > 0;
|
|
7982
8584
|
const definitions = /* @__PURE__ */ new Map();
|
|
7983
8585
|
timer.time("discover", () => {
|
|
7984
8586
|
for (const { file, root, module: moduleBinding } of bound) {
|
|
@@ -7990,6 +8592,7 @@ async function extractPythonProject(options) {
|
|
|
7990
8592
|
}
|
|
7991
8593
|
if (needsValues) {
|
|
7992
8594
|
bindEvaluator(db, { files: bound, definitions });
|
|
8595
|
+
bindEnvFacts(db, envFacts);
|
|
7993
8596
|
}
|
|
7994
8597
|
addPackWords(db, packWordsOf(options.packs));
|
|
7995
8598
|
});
|
|
@@ -8005,13 +8608,14 @@ async function extractPythonProject(options) {
|
|
|
8005
8608
|
...mountsRouters ? { facts: db } : {}
|
|
8006
8609
|
})
|
|
8007
8610
|
);
|
|
8008
|
-
const storageFor = (file) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
8611
|
+
const storageFor = (file) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 || sqlClients.length > 0 ? {
|
|
8009
8612
|
facts: db,
|
|
8010
8613
|
factsPath: file.file,
|
|
8011
8614
|
patterns: storagePatterns,
|
|
8012
8615
|
definitionAt: (key) => definitions.get(key),
|
|
8013
8616
|
couldMatch,
|
|
8014
|
-
rawSql: rawSqlPatterns
|
|
8617
|
+
rawSql: rawSqlPatterns,
|
|
8618
|
+
sqlClients
|
|
8015
8619
|
} : void 0;
|
|
8016
8620
|
const wrapperIndex = timer.time(
|
|
8017
8621
|
"discover",
|
|
@@ -8046,6 +8650,7 @@ async function extractPythonProject(options) {
|
|
|
8046
8650
|
}
|
|
8047
8651
|
const seeds = [];
|
|
8048
8652
|
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
8653
|
+
const moduleRoots = [];
|
|
8049
8654
|
for (const boundFile of bound) {
|
|
8050
8655
|
const { file, root, module: moduleBinding } = boundFile;
|
|
8051
8656
|
const displayPath = displayPathOf(file);
|
|
@@ -8078,23 +8683,17 @@ async function extractPythonProject(options) {
|
|
|
8078
8683
|
}
|
|
8079
8684
|
const loadTimeReads = timer.time(
|
|
8080
8685
|
"discover",
|
|
8081
|
-
() => envReadEffects(root, moduleBinding)
|
|
8686
|
+
() => envReadEffects(root, moduleBinding, db)
|
|
8082
8687
|
);
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
}),
|
|
8093
|
-
{ gapHandling }
|
|
8094
|
-
)
|
|
8095
|
-
);
|
|
8096
|
-
summary.confidence = { source: "inferred_static", level: "low" };
|
|
8097
|
-
summaries.push(summary);
|
|
8688
|
+
const moduleKey = nodeId(file, root);
|
|
8689
|
+
if (!summariesBySeed.has(moduleKey)) {
|
|
8690
|
+
seeds.push({ key: moduleKey, file: boundFile, node: root });
|
|
8691
|
+
moduleRoots.push({
|
|
8692
|
+
boundFile,
|
|
8693
|
+
displayPath,
|
|
8694
|
+
key: moduleKey,
|
|
8695
|
+
loadTimeReads
|
|
8696
|
+
});
|
|
8098
8697
|
}
|
|
8099
8698
|
}
|
|
8100
8699
|
const reached = timer.time(
|
|
@@ -8108,16 +8707,19 @@ async function extractPythonProject(options) {
|
|
|
8108
8707
|
definitions
|
|
8109
8708
|
})
|
|
8110
8709
|
);
|
|
8710
|
+
const placeWhatItReached = (summary, key) => {
|
|
8711
|
+
if (gapHandling !== "silent") {
|
|
8712
|
+
summary.gaps.push(
|
|
8713
|
+
...(reached.stopsByKey.get(key) ?? []).map(unfollowedCallGap2)
|
|
8714
|
+
);
|
|
8715
|
+
}
|
|
8716
|
+
placeCalls2(summary, reached.targetsByKey.get(key));
|
|
8717
|
+
placeArgTargets2(summary, reached.argTargetsByKey.get(key));
|
|
8718
|
+
placeCalleeParameters2(summary, reached.parameterCallsByKey.get(key));
|
|
8719
|
+
};
|
|
8111
8720
|
for (const [key, owners] of summariesBySeed) {
|
|
8112
8721
|
for (const summary of owners) {
|
|
8113
|
-
|
|
8114
|
-
summary.gaps.push(
|
|
8115
|
-
...(reached.stopsByKey.get(key) ?? []).map(unfollowedCallGap2)
|
|
8116
|
-
);
|
|
8117
|
-
}
|
|
8118
|
-
placeCalls2(summary, reached.targetsByKey.get(key));
|
|
8119
|
-
placeArgTargets2(summary, reached.argTargetsByKey.get(key));
|
|
8120
|
-
placeCalleeParameters2(summary, reached.parameterCallsByKey.get(key));
|
|
8722
|
+
placeWhatItReached(summary, key);
|
|
8121
8723
|
}
|
|
8122
8724
|
}
|
|
8123
8725
|
if (gapHandling !== "silent") {
|
|
@@ -8127,6 +8729,30 @@ async function extractPythonProject(options) {
|
|
|
8127
8729
|
reached.passedPositions
|
|
8128
8730
|
);
|
|
8129
8731
|
}
|
|
8732
|
+
for (const { boundFile, displayPath, key, loadTimeReads } of moduleRoots) {
|
|
8733
|
+
const calledAtLoad = reached.callsByKey.get(key) ?? [];
|
|
8734
|
+
if (loadTimeReads.length === 0 && calledAtLoad.length === 0) {
|
|
8735
|
+
continue;
|
|
8736
|
+
}
|
|
8737
|
+
const summary = timer.time(
|
|
8738
|
+
"summarize",
|
|
8739
|
+
() => assembleSummary2(
|
|
8740
|
+
moduleInitStructure({
|
|
8741
|
+
name: path3.basename(displayPath),
|
|
8742
|
+
file: displayPath,
|
|
8743
|
+
range: rangeOf(boundFile.root),
|
|
8744
|
+
effects: [
|
|
8745
|
+
...loadTimeReads,
|
|
8746
|
+
...moduleLoadInvocationEffects(boundFile.root, db).map(effectToIR)
|
|
8747
|
+
]
|
|
8748
|
+
}),
|
|
8749
|
+
{ gapHandling }
|
|
8750
|
+
)
|
|
8751
|
+
);
|
|
8752
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
8753
|
+
placeWhatItReached(summary, key);
|
|
8754
|
+
summaries.push(summary);
|
|
8755
|
+
}
|
|
8130
8756
|
summaries.push(...reached.summaries);
|
|
8131
8757
|
const resolvedImports = resolvedImportsOf(db, displayPathOf);
|
|
8132
8758
|
stampModuleImports(summaries, (file) => resolvedImports.get(file) ?? []);
|