@suss/adapter-python 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1016 -495
- package/dist/index.d.cts +255 -204
- package/dist/index.d.ts +255 -204
- package/dist/index.js +1009 -483
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -24,6 +24,11 @@ function isType(node, ...types) {
|
|
|
24
24
|
function isFunction(node) {
|
|
25
25
|
return node.type === "function_definition" || node.type === "lambda";
|
|
26
26
|
}
|
|
27
|
+
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
28
|
+
"function_definition",
|
|
29
|
+
"lambda",
|
|
30
|
+
"class_definition"
|
|
31
|
+
]);
|
|
27
32
|
function enclosingFunction(node) {
|
|
28
33
|
let current = node.parent;
|
|
29
34
|
while (current !== null) {
|
|
@@ -93,6 +98,25 @@ function children(node) {
|
|
|
93
98
|
function bodyStatements(body) {
|
|
94
99
|
return children(body);
|
|
95
100
|
}
|
|
101
|
+
function nestedStatements(stmt) {
|
|
102
|
+
const found = [];
|
|
103
|
+
for (const child of children(stmt)) {
|
|
104
|
+
if (child.type === "block") {
|
|
105
|
+
for (const inner of children(child)) {
|
|
106
|
+
if (inner.type.endsWith("_clause")) {
|
|
107
|
+
found.push(...nestedStatements(inner));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
found.push(inner);
|
|
111
|
+
}
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (child.type.endsWith("_clause")) {
|
|
115
|
+
found.push(...nestedStatements(child));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return found;
|
|
119
|
+
}
|
|
96
120
|
function stripDecorators(node) {
|
|
97
121
|
if (node.type !== "decorated_definition") {
|
|
98
122
|
return { definition: node, decorators: [] };
|
|
@@ -453,8 +477,36 @@ function resolveNonlocal(scope, name) {
|
|
|
453
477
|
}
|
|
454
478
|
|
|
455
479
|
// src/annotations.ts
|
|
456
|
-
function createAnnotationContext(scopeFor) {
|
|
457
|
-
return {
|
|
480
|
+
function createAnnotationContext(scopeFor, importedDefinition = null) {
|
|
481
|
+
return {
|
|
482
|
+
scopeMaps: [scopeFor],
|
|
483
|
+
definitions: /* @__PURE__ */ new Map(),
|
|
484
|
+
importedDefinition,
|
|
485
|
+
expanding: /* @__PURE__ */ new Set()
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function scopeOfNode(ctx, node) {
|
|
489
|
+
for (const scopeFor of ctx.scopeMaps) {
|
|
490
|
+
const scope = scopeFor.get(node.id);
|
|
491
|
+
if (scope !== void 0) {
|
|
492
|
+
return scope;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
function aliasValueOf(name, scope, ctx) {
|
|
498
|
+
const binding = resolveName(scope, name);
|
|
499
|
+
if (binding?.kind === "assignment") {
|
|
500
|
+
return binding.value === null ? null : { node: binding.value, scope };
|
|
501
|
+
}
|
|
502
|
+
if (binding?.kind !== "import" && binding?.kind !== "importFrom") {
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
const imported = ctx.importedDefinition?.(scope, name) ?? null;
|
|
506
|
+
if (imported === null || imported.node.type === "class_definition") {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
return { node: imported.node, scope: imported.moduleScope };
|
|
458
510
|
}
|
|
459
511
|
function collectedDefinitions(ctx) {
|
|
460
512
|
const out = {};
|
|
@@ -497,6 +549,7 @@ var EXPRESSION_SHAPERS = {
|
|
|
497
549
|
identifier: (node, scope, ctx) => shapeFromName(node.text, scope, ctx),
|
|
498
550
|
none: () => ({ type: "null" }),
|
|
499
551
|
generic_type: shapeFromGenericType,
|
|
552
|
+
subscript: shapeFromSubscript,
|
|
500
553
|
binary_operator: shapeFromBinaryOperator
|
|
501
554
|
};
|
|
502
555
|
function shapeFromExpression(node, scope, ctx) {
|
|
@@ -518,8 +571,46 @@ function shapeFromName(name, scope, ctx) {
|
|
|
518
571
|
if (binding?.kind === "classDef") {
|
|
519
572
|
return recordShapeRef(name, binding.node, ctx);
|
|
520
573
|
}
|
|
574
|
+
if (binding?.kind === "import" || binding?.kind === "importFrom") {
|
|
575
|
+
const imported = ctx.importedDefinition?.(scope, name) ?? null;
|
|
576
|
+
if (imported?.node.type === "class_definition") {
|
|
577
|
+
if (!ctx.scopeMaps.includes(imported.scopeFor)) {
|
|
578
|
+
ctx.scopeMaps.push(imported.scopeFor);
|
|
579
|
+
}
|
|
580
|
+
return recordShapeRef(name, imported.node, ctx);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
const alias = aliasValueOf(name, scope, ctx);
|
|
584
|
+
if (alias !== null) {
|
|
585
|
+
return shapeFromAliasValue(name, alias, ctx);
|
|
586
|
+
}
|
|
521
587
|
return { type: "ref", name };
|
|
522
588
|
}
|
|
589
|
+
function shapeFromAliasValue(name, alias, ctx) {
|
|
590
|
+
if (ctx.expanding.has(alias.node.id)) {
|
|
591
|
+
return { type: "ref", name };
|
|
592
|
+
}
|
|
593
|
+
ctx.expanding.add(alias.node.id);
|
|
594
|
+
try {
|
|
595
|
+
return shapeFromExpression(alias.node, alias.scope, ctx);
|
|
596
|
+
} finally {
|
|
597
|
+
ctx.expanding.delete(alias.node.id);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function shapeFromSubscript(node, scope, ctx) {
|
|
601
|
+
const base = field(node, "value");
|
|
602
|
+
const args = fields(node, "subscript");
|
|
603
|
+
const baseName = base?.type === "identifier" ? base.text : null;
|
|
604
|
+
if (baseName === "Annotated") {
|
|
605
|
+
const first = args[0];
|
|
606
|
+
return first === void 0 ? { type: "unknown" } : shapeFromExpression(first, scope, ctx);
|
|
607
|
+
}
|
|
608
|
+
return shapeFromGeneric(
|
|
609
|
+
baseName,
|
|
610
|
+
node.text,
|
|
611
|
+
args.map((arg) => shapeFromExpression(arg, scope, ctx))
|
|
612
|
+
);
|
|
613
|
+
}
|
|
523
614
|
function genericTypeArgs(node) {
|
|
524
615
|
const typeParameter = node.namedChildren.find(
|
|
525
616
|
(child) => child !== null && child.type === "type_parameter"
|
|
@@ -530,14 +621,20 @@ function genericTypeArgs(node) {
|
|
|
530
621
|
}
|
|
531
622
|
function shapeFromGenericType(node, scope, ctx) {
|
|
532
623
|
const base = node.namedChild(0);
|
|
533
|
-
const args = genericTypeArgs(node);
|
|
534
|
-
const shapeOf = (arg) => arg !== void 0 ? annotationToShape(arg, scope, ctx) : { type: "unknown" };
|
|
535
624
|
const baseName = base?.type === "identifier" ? base.text : null;
|
|
625
|
+
return shapeFromGeneric(
|
|
626
|
+
baseName,
|
|
627
|
+
node.text,
|
|
628
|
+
genericTypeArgs(node).map((arg) => annotationToShape(arg, scope, ctx))
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
function shapeFromGeneric(baseName, text2, args) {
|
|
632
|
+
const shapeOf = (arg) => arg ?? { type: "unknown" };
|
|
536
633
|
if (baseName === "Optional") {
|
|
537
634
|
return { type: "union", variants: [shapeOf(args[0]), { type: "null" }] };
|
|
538
635
|
}
|
|
539
636
|
if (baseName === "Union") {
|
|
540
|
-
return { type: "union", variants: args
|
|
637
|
+
return { type: "union", variants: args };
|
|
541
638
|
}
|
|
542
639
|
if (baseName !== null && LIST_NAMES.has(baseName)) {
|
|
543
640
|
return { type: "array", items: shapeOf(args[0]) };
|
|
@@ -548,7 +645,7 @@ function shapeFromGenericType(node, scope, ctx) {
|
|
|
548
645
|
if (baseName !== null && TUPLE_SET_NAMES.has(baseName)) {
|
|
549
646
|
return { type: "array", items: shapeOf(args[0]) };
|
|
550
647
|
}
|
|
551
|
-
return { type: "ref", name: baseName ??
|
|
648
|
+
return { type: "ref", name: baseName ?? text2 };
|
|
552
649
|
}
|
|
553
650
|
function shapeFromBinaryOperator(node, scope, ctx) {
|
|
554
651
|
const operator = field(node, "operator");
|
|
@@ -573,7 +670,7 @@ function recordShapeRef(name, classNode, ctx) {
|
|
|
573
670
|
}
|
|
574
671
|
ctx.definitions.set(key, null);
|
|
575
672
|
const bodyNode = field(classNode, "body");
|
|
576
|
-
const classScope = ctx
|
|
673
|
+
const classScope = scopeOfNode(ctx, classNode);
|
|
577
674
|
ctx.definitions.set(
|
|
578
675
|
key,
|
|
579
676
|
bodyNode !== null && classScope !== void 0 ? recordShapeOf(bodyNode, classScope, ctx) : { type: "record", properties: {} }
|
|
@@ -1316,14 +1413,14 @@ function invocationEffects(definitionNode) {
|
|
|
1316
1413
|
if (calls.length === 0) {
|
|
1317
1414
|
return [];
|
|
1318
1415
|
}
|
|
1319
|
-
const
|
|
1416
|
+
const statementOf3 = /* @__PURE__ */ new Map();
|
|
1320
1417
|
const statements = [];
|
|
1321
1418
|
for (const call of calls) {
|
|
1322
1419
|
const statement = enclosingStatement(call, body);
|
|
1323
1420
|
if (statement === null) {
|
|
1324
1421
|
continue;
|
|
1325
1422
|
}
|
|
1326
|
-
|
|
1423
|
+
statementOf3.set(call.id, statement);
|
|
1327
1424
|
statements.push(statement);
|
|
1328
1425
|
}
|
|
1329
1426
|
const lowered = lowerPythonBody(body, statements);
|
|
@@ -1335,7 +1432,7 @@ function invocationEffects(definitionNode) {
|
|
|
1335
1432
|
statements
|
|
1336
1433
|
);
|
|
1337
1434
|
return calls.map((call) => {
|
|
1338
|
-
const statement =
|
|
1435
|
+
const statement = statementOf3.get(call.id);
|
|
1339
1436
|
const conditions = sharedGatingConditions(
|
|
1340
1437
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement),
|
|
1341
1438
|
predicateOf
|
|
@@ -1354,44 +1451,30 @@ function invocationEffects(definitionNode) {
|
|
|
1354
1451
|
import { mountPathsOf } from "@suss/resolution";
|
|
1355
1452
|
|
|
1356
1453
|
// src/facts/resolve.ts
|
|
1357
|
-
import { deriveOnDemand, evaluate } from "@suss/datalog";
|
|
1358
1454
|
import {
|
|
1359
|
-
|
|
1455
|
+
askResolution,
|
|
1360
1456
|
placeholderValues,
|
|
1361
|
-
RESOLUTION_QUESTIONS,
|
|
1362
|
-
RESOLUTION_RULES,
|
|
1363
1457
|
writtenValueOf as sharedWrittenValueOf,
|
|
1364
1458
|
singleAnswers
|
|
1365
1459
|
} from "@suss/resolution";
|
|
1366
|
-
var RESOLUTION_PROGRAM = deriveOnDemand(
|
|
1367
|
-
[...RESOLUTION_RULES, ...RESOLUTION_QUESTIONS],
|
|
1368
|
-
ANSWER_RELATIONS
|
|
1369
|
-
);
|
|
1370
1460
|
function resolveCalls(db, callKeys) {
|
|
1371
|
-
|
|
1372
|
-
const fresh = callKeys.filter((key) => !asked.has(key));
|
|
1373
|
-
if (fresh.length === 0) {
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
for (const callKey of fresh) {
|
|
1377
|
-
db.add("wanted", [callKey]);
|
|
1378
|
-
}
|
|
1379
|
-
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1461
|
+
askResolution(db, callKeys);
|
|
1380
1462
|
}
|
|
1381
1463
|
function writtenValueOf(db, key) {
|
|
1382
1464
|
resolveCalls(db, [key]);
|
|
1383
1465
|
return sharedWrittenValueOf(db, key, (keys) => resolveCalls(db, keys));
|
|
1384
1466
|
}
|
|
1467
|
+
function originsOf(db, nameKey2) {
|
|
1468
|
+
askResolution(db, [nameKey2], "wantedOrigin");
|
|
1469
|
+
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey2).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
1470
|
+
}
|
|
1385
1471
|
function subjectConstructions(db, valueKeys) {
|
|
1386
1472
|
const found = /* @__PURE__ */ new Map();
|
|
1387
1473
|
if (valueKeys.length === 0) {
|
|
1388
1474
|
return found;
|
|
1389
1475
|
}
|
|
1390
1476
|
const askSubjects = (keys) => {
|
|
1391
|
-
|
|
1392
|
-
db.add("wantedSubject", [key]);
|
|
1393
|
-
}
|
|
1394
|
-
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1477
|
+
askResolution(db, keys, "wantedSubject");
|
|
1395
1478
|
};
|
|
1396
1479
|
askSubjects(valueKeys);
|
|
1397
1480
|
const placeholders = placeholderValues(db);
|
|
@@ -1438,6 +1521,11 @@ function objectReturnedBy(db, callKey) {
|
|
|
1438
1521
|
}
|
|
1439
1522
|
|
|
1440
1523
|
// src/facts/values.ts
|
|
1524
|
+
import {
|
|
1525
|
+
startsAtName,
|
|
1526
|
+
valueLeftByWrites,
|
|
1527
|
+
writesRunInOrder
|
|
1528
|
+
} from "@suss/resolution";
|
|
1441
1529
|
function nodeId(filePath, node) {
|
|
1442
1530
|
return `${filePath}:${node.startIndex}-${node.endIndex}`;
|
|
1443
1531
|
}
|
|
@@ -1448,10 +1536,10 @@ function readKey(filePath, node, enclosing) {
|
|
|
1448
1536
|
if (node.type !== "identifier") {
|
|
1449
1537
|
return nodeId(filePath, node);
|
|
1450
1538
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
return
|
|
1539
|
+
return nameKeyIn(filePath, enclosing, node.text);
|
|
1540
|
+
}
|
|
1541
|
+
function nameKeyIn(filePath, enclosing, name) {
|
|
1542
|
+
return nameKey(filePath, scopeChainOf(filePath, enclosing), name);
|
|
1455
1543
|
}
|
|
1456
1544
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
1457
1545
|
"string",
|
|
@@ -1489,9 +1577,6 @@ function parameterName2(param) {
|
|
|
1489
1577
|
}
|
|
1490
1578
|
return children(param).find((child) => child.type === "identifier") ?? null;
|
|
1491
1579
|
}
|
|
1492
|
-
function parameterList(fn) {
|
|
1493
|
-
return parameterShapes(fn).map((parameter) => parameter.name);
|
|
1494
|
-
}
|
|
1495
1580
|
function parameterShapes(fn) {
|
|
1496
1581
|
const params = field(fn, "parameters");
|
|
1497
1582
|
const declared = [];
|
|
@@ -1506,24 +1591,52 @@ function parameterShapes(fn) {
|
|
|
1506
1591
|
}
|
|
1507
1592
|
return declared;
|
|
1508
1593
|
}
|
|
1509
|
-
function
|
|
1510
|
-
|
|
1594
|
+
function boundParameterNames(fn) {
|
|
1595
|
+
const params = field(fn, "parameters");
|
|
1596
|
+
const names = [];
|
|
1597
|
+
for (const param of params === null ? [] : children(params)) {
|
|
1598
|
+
const name = parameterName2(param);
|
|
1599
|
+
if (name !== null) {
|
|
1600
|
+
names.push(name.text);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return names;
|
|
1511
1604
|
}
|
|
1512
1605
|
function add(emitter, relation, ...tuple) {
|
|
1513
1606
|
emitter.db.add(relation, tuple);
|
|
1514
1607
|
}
|
|
1608
|
+
function nameKey(filePath, scope, name) {
|
|
1609
|
+
for (let current = scope; current !== null; current = current.parent) {
|
|
1610
|
+
if (current.globals.has(name)) {
|
|
1611
|
+
return nameId(filePath, name);
|
|
1612
|
+
}
|
|
1613
|
+
if (current.locals.has(name)) {
|
|
1614
|
+
return `${current.funcKey}#${name}`;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
return nameId(filePath, name);
|
|
1618
|
+
}
|
|
1619
|
+
function scopeChainOf(filePath, fn) {
|
|
1620
|
+
if (fn === null) {
|
|
1621
|
+
return null;
|
|
1622
|
+
}
|
|
1623
|
+
const reading = scopeReadingOf(fn);
|
|
1624
|
+
return {
|
|
1625
|
+
funcKey: nodeId(filePath, fn),
|
|
1626
|
+
locals: reading.locals,
|
|
1627
|
+
globals: reading.globals,
|
|
1628
|
+
parent: scopeChainOf(filePath, enclosingFunction(fn))
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1515
1631
|
function emitCall(emitter, call) {
|
|
1516
1632
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
1517
1633
|
const callee = field(call, "function");
|
|
1518
|
-
|
|
1634
|
+
const args = field(call, "arguments");
|
|
1635
|
+
if (callee === null || args === null) {
|
|
1519
1636
|
return;
|
|
1520
1637
|
}
|
|
1521
1638
|
const callKey = nodeId(emitter.filePath, call);
|
|
1522
1639
|
add(emitter, "call", callKey, valueKey(emitter, callee));
|
|
1523
|
-
const args = field(call, "arguments");
|
|
1524
|
-
if (args === null) {
|
|
1525
|
-
return;
|
|
1526
|
-
}
|
|
1527
1640
|
let position = 0;
|
|
1528
1641
|
for (const argument of children(args)) {
|
|
1529
1642
|
if (argument.type === "keyword_argument") {
|
|
@@ -1554,11 +1667,7 @@ function valueKey(emitter, value) {
|
|
|
1554
1667
|
if (value.type !== "identifier") {
|
|
1555
1668
|
return nodeId(emitter.filePath, value);
|
|
1556
1669
|
}
|
|
1557
|
-
|
|
1558
|
-
if (enclosing !== null && enclosing.params.has(value.text)) {
|
|
1559
|
-
return `${enclosing.funcKey}#${value.text}`;
|
|
1560
|
-
}
|
|
1561
|
-
return nameId(emitter.filePath, value.text);
|
|
1670
|
+
return nameKey(emitter.filePath, emitter.enclosing, value.text);
|
|
1562
1671
|
}
|
|
1563
1672
|
function emitSequence(emitter, sequence) {
|
|
1564
1673
|
const objectKey = nodeId(emitter.filePath, sequence);
|
|
@@ -1648,7 +1757,6 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1648
1757
|
const funcKey = nodeId(emitter.filePath, fn);
|
|
1649
1758
|
add(emitter, "func", funcKey);
|
|
1650
1759
|
const params = field(fn, "parameters");
|
|
1651
|
-
const declared = parameterNames(fn);
|
|
1652
1760
|
let position = classKey === void 0 ? 0 : -1;
|
|
1653
1761
|
let byPosition = true;
|
|
1654
1762
|
let receiver = null;
|
|
@@ -1671,24 +1779,22 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1671
1779
|
}
|
|
1672
1780
|
position += 1;
|
|
1673
1781
|
}
|
|
1782
|
+
const reading = scopeReadingOf(fn);
|
|
1674
1783
|
const inside = {
|
|
1675
1784
|
...emitter,
|
|
1676
|
-
enclosing: {
|
|
1785
|
+
enclosing: {
|
|
1786
|
+
funcKey,
|
|
1787
|
+
locals: reading.locals,
|
|
1788
|
+
globals: reading.globals,
|
|
1789
|
+
parent: emitter.enclosing
|
|
1790
|
+
}
|
|
1677
1791
|
};
|
|
1792
|
+
emitScopeWrites(inside, reading, false);
|
|
1678
1793
|
const body = field(fn, "body");
|
|
1679
1794
|
if (body === null) {
|
|
1680
1795
|
return funcKey;
|
|
1681
1796
|
}
|
|
1682
|
-
|
|
1683
|
-
for (const child of children(node)) {
|
|
1684
|
-
if (isFunction(child)) {
|
|
1685
|
-
add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
|
|
1686
|
-
continue;
|
|
1687
|
-
}
|
|
1688
|
-
recordNested(child);
|
|
1689
|
-
}
|
|
1690
|
-
};
|
|
1691
|
-
recordNested(body);
|
|
1797
|
+
emitNestedDefinitions(inside, body);
|
|
1692
1798
|
walkExpressions(inside, body, (child) => {
|
|
1693
1799
|
if (child.type === "return_statement") {
|
|
1694
1800
|
const returned = child.namedChildren[0];
|
|
@@ -1699,29 +1805,17 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1699
1805
|
if (child.type === "call") {
|
|
1700
1806
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1701
1807
|
}
|
|
1702
|
-
if (child.type === "assignment") {
|
|
1703
|
-
|
|
1808
|
+
if (child.type === "assignment" && receiver !== null) {
|
|
1809
|
+
emitReceiverProperty(inside, child, receiver);
|
|
1704
1810
|
}
|
|
1705
1811
|
emitExpressionFact(inside, child);
|
|
1706
1812
|
});
|
|
1707
1813
|
return funcKey;
|
|
1708
1814
|
}
|
|
1709
|
-
function
|
|
1815
|
+
function emitReceiverProperty(emitter, assignment2, receiver) {
|
|
1710
1816
|
const left = field(assignment2, "left");
|
|
1711
1817
|
const right = field(assignment2, "right");
|
|
1712
|
-
if (left === null || right === null) {
|
|
1713
|
-
return;
|
|
1714
|
-
}
|
|
1715
|
-
if (left.type === "identifier") {
|
|
1716
|
-
add(
|
|
1717
|
-
emitter,
|
|
1718
|
-
"binds",
|
|
1719
|
-
nameId(emitter.filePath, left.text),
|
|
1720
|
-
valueKey(emitter, right)
|
|
1721
|
-
);
|
|
1722
|
-
return;
|
|
1723
|
-
}
|
|
1724
|
-
if (receiver === null || left.type !== "attribute") {
|
|
1818
|
+
if (left === null || right === null || left.type !== "attribute") {
|
|
1725
1819
|
return;
|
|
1726
1820
|
}
|
|
1727
1821
|
const object = field(left, "object");
|
|
@@ -1737,20 +1831,13 @@ function emitBinding(emitter, assignment2, receiver) {
|
|
|
1737
1831
|
valueKey(emitter, right)
|
|
1738
1832
|
);
|
|
1739
1833
|
}
|
|
1740
|
-
function
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
const right = field(assignment2, "right");
|
|
1744
|
-
if (left === null || right === null || left.type !== "identifier") {
|
|
1745
|
-
return;
|
|
1834
|
+
function assignedValue(right) {
|
|
1835
|
+
if (right.type !== "subscript") {
|
|
1836
|
+
return right;
|
|
1746
1837
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
emitter.filePath,
|
|
1751
|
-
left.text,
|
|
1752
|
-
valueKey(emitter, right)
|
|
1753
|
-
);
|
|
1838
|
+
const outer = field(right, "value");
|
|
1839
|
+
const first = fields(right, "subscript")[0];
|
|
1840
|
+
return outer?.text === "Annotated" && first !== void 0 ? first : right;
|
|
1754
1841
|
}
|
|
1755
1842
|
function declaredBy(statement) {
|
|
1756
1843
|
if (statement.type === "decorated_definition") {
|
|
@@ -1794,40 +1881,361 @@ function emitClassFacts(emitter, cls) {
|
|
|
1794
1881
|
}
|
|
1795
1882
|
continue;
|
|
1796
1883
|
}
|
|
1797
|
-
const
|
|
1884
|
+
const memberKey2 = declaredMemberKey(emitter, member, classKey);
|
|
1798
1885
|
const name = field(member, "name");
|
|
1799
|
-
if (
|
|
1800
|
-
add(emitter, "holdsProperty", classKey, name.text,
|
|
1886
|
+
if (memberKey2 !== null && name !== null) {
|
|
1887
|
+
add(emitter, "holdsProperty", classKey, name.text, memberKey2);
|
|
1801
1888
|
}
|
|
1802
1889
|
}
|
|
1803
1890
|
return classKey;
|
|
1804
1891
|
}
|
|
1805
|
-
function
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1892
|
+
function emitNestedDefinitions(emitter, node) {
|
|
1893
|
+
for (const child of children(node)) {
|
|
1894
|
+
if (isFunction(child)) {
|
|
1895
|
+
emitFunctionFacts(emitter, child);
|
|
1896
|
+
if (emitter.enclosing !== null) {
|
|
1897
|
+
add(
|
|
1898
|
+
emitter,
|
|
1899
|
+
"containsFn",
|
|
1900
|
+
emitter.enclosing.funcKey,
|
|
1901
|
+
nodeId(emitter.filePath, child)
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
if (child.type === "class_definition") {
|
|
1907
|
+
emitClassFacts(emitter, child);
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
emitNestedDefinitions(emitter, child);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
var readingsByTree = /* @__PURE__ */ new WeakMap();
|
|
1914
|
+
function scopeReadingOf(fn) {
|
|
1915
|
+
const tree = fn.tree;
|
|
1916
|
+
let byNode = readingsByTree.get(tree);
|
|
1917
|
+
if (byNode === void 0) {
|
|
1918
|
+
byNode = /* @__PURE__ */ new Map();
|
|
1919
|
+
readingsByTree.set(tree, byNode);
|
|
1920
|
+
}
|
|
1921
|
+
const remembered = byNode.get(fn.id);
|
|
1922
|
+
if (remembered !== void 0) {
|
|
1923
|
+
return remembered;
|
|
1924
|
+
}
|
|
1925
|
+
const body = field(fn, "body");
|
|
1926
|
+
const reading = readScope(
|
|
1927
|
+
body !== null && body.type === "block" ? body : null,
|
|
1928
|
+
boundParameterNames(fn)
|
|
1929
|
+
);
|
|
1930
|
+
byNode.set(fn.id, reading);
|
|
1931
|
+
return reading;
|
|
1932
|
+
}
|
|
1933
|
+
var TARGET_PATTERN_TYPES = /* @__PURE__ */ new Set([
|
|
1934
|
+
"pattern_list",
|
|
1935
|
+
"tuple_pattern",
|
|
1936
|
+
"list_pattern"
|
|
1937
|
+
]);
|
|
1938
|
+
function targetNames(target, found = []) {
|
|
1939
|
+
if (target.type === "identifier") {
|
|
1940
|
+
found.push(target);
|
|
1941
|
+
return found;
|
|
1942
|
+
}
|
|
1943
|
+
if (TARGET_PATTERN_TYPES.has(target.type)) {
|
|
1944
|
+
for (const child of children(target)) {
|
|
1945
|
+
targetNames(child, found);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return found;
|
|
1949
|
+
}
|
|
1950
|
+
function readAssignment(node, sink) {
|
|
1951
|
+
const left = field(node, "left");
|
|
1952
|
+
const right = field(node, "right");
|
|
1953
|
+
if (left === null) {
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
const written = right === null ? null : assignedValue(right);
|
|
1957
|
+
const names = targetNames(left);
|
|
1958
|
+
for (const name of names) {
|
|
1959
|
+
sink.record(name.text, {
|
|
1960
|
+
// An unpacked target takes one piece of the value, and which piece
|
|
1961
|
+
// is not something the assignment says.
|
|
1962
|
+
value: names.length === 1 ? written : null,
|
|
1963
|
+
given: null,
|
|
1964
|
+
at: node,
|
|
1965
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function readAugmentedAssignment(node, sink) {
|
|
1970
|
+
const left = field(node, "left");
|
|
1971
|
+
if (left === null || left.type !== "identifier") {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
sink.record(left.text, {
|
|
1975
|
+
value: null,
|
|
1976
|
+
given: null,
|
|
1977
|
+
at: node,
|
|
1978
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
function readIterationTarget(node, sink) {
|
|
1982
|
+
const left = field(node, "left");
|
|
1983
|
+
if (left === null) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
for (const name of targetNames(left)) {
|
|
1987
|
+
sink.record(name.text, {
|
|
1988
|
+
value: null,
|
|
1989
|
+
given: null,
|
|
1990
|
+
at: node,
|
|
1991
|
+
direct: false
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
function identifierOf(node) {
|
|
1996
|
+
if (node.type === "identifier") {
|
|
1997
|
+
return node;
|
|
1998
|
+
}
|
|
1999
|
+
return children(node).find((child) => child.type === "identifier") ?? null;
|
|
2000
|
+
}
|
|
2001
|
+
function readAsPattern(node, sink) {
|
|
2002
|
+
const target = field(node, "alias") ?? children(node)[1];
|
|
2003
|
+
const name = target === void 0 ? null : identifierOf(target);
|
|
2004
|
+
if (name === null) {
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
sink.record(name.text, {
|
|
2008
|
+
value: null,
|
|
2009
|
+
given: null,
|
|
2010
|
+
at: node,
|
|
2011
|
+
direct: false
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
function readWalrus(node, sink) {
|
|
2015
|
+
const name = field(node, "name");
|
|
2016
|
+
if (name === null || name.type !== "identifier") {
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
sink.record(name.text, {
|
|
2020
|
+
value: field(node, "value"),
|
|
2021
|
+
given: null,
|
|
2022
|
+
at: node,
|
|
2023
|
+
direct: false
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
function readGlobalStatement(node, sink) {
|
|
2027
|
+
for (const child of children(node)) {
|
|
2028
|
+
if (child.type === "identifier") {
|
|
2029
|
+
sink.globals.add(child.text);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function readNonlocalStatement(node, sink) {
|
|
2034
|
+
for (const child of children(node)) {
|
|
2035
|
+
if (child.type === "identifier") {
|
|
2036
|
+
sink.nonlocals.add(child.text);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
var WRITE_READERS = {
|
|
2041
|
+
assignment: readAssignment,
|
|
2042
|
+
augmented_assignment: readAugmentedAssignment,
|
|
2043
|
+
for_statement: readIterationTarget,
|
|
2044
|
+
for_in_clause: readIterationTarget,
|
|
2045
|
+
as_pattern: readAsPattern,
|
|
2046
|
+
named_expression: readWalrus,
|
|
2047
|
+
global_statement: readGlobalStatement,
|
|
2048
|
+
nonlocal_statement: readNonlocalStatement
|
|
2049
|
+
};
|
|
2050
|
+
function readScope(bodyOwner, parameterNames) {
|
|
2051
|
+
const writes = /* @__PURE__ */ new Map();
|
|
2052
|
+
const globals = /* @__PURE__ */ new Set();
|
|
2053
|
+
const nonlocals = /* @__PURE__ */ new Set();
|
|
2054
|
+
const record = (name, write) => {
|
|
2055
|
+
const existing = writes.get(name);
|
|
2056
|
+
if (existing === void 0) {
|
|
2057
|
+
writes.set(name, [write]);
|
|
1810
2058
|
return;
|
|
1811
2059
|
}
|
|
1812
|
-
|
|
1813
|
-
add(emitter, "exportsAs", filePath, name.text, key);
|
|
2060
|
+
existing.push(write);
|
|
1814
2061
|
};
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
2062
|
+
const sink = { bodyOwner, globals, nonlocals, record };
|
|
2063
|
+
const visit = (node) => {
|
|
2064
|
+
if (isFunction(node) || node.type === "class_definition") {
|
|
2065
|
+
const name = field(node, "name");
|
|
2066
|
+
if (name !== null) {
|
|
2067
|
+
record(name.text, {
|
|
2068
|
+
value: node,
|
|
2069
|
+
given: null,
|
|
2070
|
+
at: node,
|
|
2071
|
+
direct: isDirectStatement(node, bodyOwner)
|
|
2072
|
+
});
|
|
1826
2073
|
}
|
|
1827
|
-
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
WRITE_READERS[node.type]?.(node, sink);
|
|
2077
|
+
for (const child of children(node)) {
|
|
2078
|
+
visit(child);
|
|
1828
2079
|
}
|
|
1829
2080
|
};
|
|
1830
|
-
|
|
2081
|
+
for (const statement of bodyOwner === null ? [] : children(bodyOwner)) {
|
|
2082
|
+
visit(statement);
|
|
2083
|
+
}
|
|
2084
|
+
const locals = new Set(writes.keys());
|
|
2085
|
+
for (const name of [...globals, ...nonlocals]) {
|
|
2086
|
+
locals.delete(name);
|
|
2087
|
+
}
|
|
2088
|
+
for (const name of parameterNames) {
|
|
2089
|
+
locals.add(name);
|
|
2090
|
+
addParameterWrite(writes.get(name), name);
|
|
2091
|
+
}
|
|
2092
|
+
return { bodyOwner, writes, locals, globals };
|
|
2093
|
+
}
|
|
2094
|
+
function addParameterWrite(later, name) {
|
|
2095
|
+
const first = later?.[0];
|
|
2096
|
+
if (later === void 0 || first === void 0) {
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
later.unshift({ value: null, given: name, at: first.at, direct: false });
|
|
2100
|
+
}
|
|
2101
|
+
function statementOf(node) {
|
|
2102
|
+
let current = node;
|
|
2103
|
+
while (current !== null) {
|
|
2104
|
+
const parent = current.parent;
|
|
2105
|
+
if (parent === null) {
|
|
2106
|
+
return null;
|
|
2107
|
+
}
|
|
2108
|
+
if (parent.type === "block" || parent.type === "module") {
|
|
2109
|
+
return current;
|
|
2110
|
+
}
|
|
2111
|
+
current = parent;
|
|
2112
|
+
}
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
function isDirectStatement(node, bodyOwner) {
|
|
2116
|
+
if (bodyOwner === null) {
|
|
2117
|
+
return false;
|
|
2118
|
+
}
|
|
2119
|
+
const statement = statementOf(node);
|
|
2120
|
+
return statement !== null && statement.parent?.id === bodyOwner.id;
|
|
2121
|
+
}
|
|
2122
|
+
var UNREAD_PARENTS = /* @__PURE__ */ new Set([
|
|
2123
|
+
"as_pattern_target",
|
|
2124
|
+
"global_statement",
|
|
2125
|
+
"nonlocal_statement",
|
|
2126
|
+
"pattern_list",
|
|
2127
|
+
"tuple_pattern",
|
|
2128
|
+
"list_pattern",
|
|
2129
|
+
"dotted_name"
|
|
2130
|
+
]);
|
|
2131
|
+
var UNREAD_FIELDS = {
|
|
2132
|
+
assignment: "left",
|
|
2133
|
+
augmented_assignment: "left",
|
|
2134
|
+
for_statement: "left",
|
|
2135
|
+
for_in_clause: "left",
|
|
2136
|
+
named_expression: "name",
|
|
2137
|
+
function_definition: "name",
|
|
2138
|
+
class_definition: "name",
|
|
2139
|
+
attribute: "attribute",
|
|
2140
|
+
keyword_argument: "name"
|
|
2141
|
+
};
|
|
2142
|
+
function isNameRead(name) {
|
|
2143
|
+
const parent = name.parent;
|
|
2144
|
+
if (parent === null) {
|
|
2145
|
+
return true;
|
|
2146
|
+
}
|
|
2147
|
+
if (UNREAD_PARENTS.has(parent.type)) {
|
|
2148
|
+
return false;
|
|
2149
|
+
}
|
|
2150
|
+
const fieldName = UNREAD_FIELDS[parent.type];
|
|
2151
|
+
return fieldName === void 0 || field(parent, fieldName)?.id !== name.id;
|
|
2152
|
+
}
|
|
2153
|
+
var READ_FIRST = {
|
|
2154
|
+
call: "function",
|
|
2155
|
+
attribute: "object"
|
|
2156
|
+
};
|
|
2157
|
+
function readFirst(node) {
|
|
2158
|
+
if (node.type === "parenthesized_expression") {
|
|
2159
|
+
return children(node)[0] ?? null;
|
|
2160
|
+
}
|
|
2161
|
+
const fieldName = READ_FIRST[node.type];
|
|
2162
|
+
return fieldName === void 0 ? null : field(node, fieldName);
|
|
2163
|
+
}
|
|
2164
|
+
var NAME_READS = {
|
|
2165
|
+
nameTypes: /* @__PURE__ */ new Set(["identifier"]),
|
|
2166
|
+
laterBodies: LATER_BODY_TYPES,
|
|
2167
|
+
childrenOf: children,
|
|
2168
|
+
isRead: isNameRead,
|
|
2169
|
+
readFirst
|
|
2170
|
+
};
|
|
2171
|
+
var CONSTRUCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2172
|
+
"call",
|
|
2173
|
+
"list",
|
|
2174
|
+
"dictionary",
|
|
2175
|
+
"set",
|
|
2176
|
+
"tuple",
|
|
2177
|
+
"list_comprehension",
|
|
2178
|
+
"dictionary_comprehension",
|
|
2179
|
+
"set_comprehension",
|
|
2180
|
+
"generator_expression"
|
|
2181
|
+
]);
|
|
2182
|
+
function describeWrite(emitter, write, name) {
|
|
2183
|
+
if (write.value === null) {
|
|
2184
|
+
return {
|
|
2185
|
+
value: write.given,
|
|
2186
|
+
placeholder: false,
|
|
2187
|
+
construction: null,
|
|
2188
|
+
narrowsName: false
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
return {
|
|
2192
|
+
value: valueKey(emitter, write.value),
|
|
2193
|
+
placeholder: write.value.type === "none",
|
|
2194
|
+
construction: CONSTRUCTION_TYPES.has(write.value.type) ? write.value.text.replace(/\s+/g, " ").trim() : null,
|
|
2195
|
+
narrowsName: write.value.type === "call" && startsAtName(write.value, name, NAME_READS)
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function settledValue(emitter, reading, name, writes) {
|
|
2199
|
+
const only = writes.length === 1 ? writes[0] : void 0;
|
|
2200
|
+
if (only !== void 0) {
|
|
2201
|
+
return only.value === null ? only.given : valueKey(emitter, only.value);
|
|
2202
|
+
}
|
|
2203
|
+
return valueLeftByWrites(
|
|
2204
|
+
writes.map((write) => describeWrite(emitter, write, name)),
|
|
2205
|
+
writesRunInOrder(reading.bodyOwner, name, writes, NAME_READS)
|
|
2206
|
+
);
|
|
2207
|
+
}
|
|
2208
|
+
function emitScopeWrites(emitter, reading, atModule) {
|
|
2209
|
+
for (const [name, writes] of reading.writes) {
|
|
2210
|
+
const settled = settledValue(emitter, reading, name, writes);
|
|
2211
|
+
const key = nameKey(emitter.filePath, emitter.enclosing, name);
|
|
2212
|
+
if (settled === null) {
|
|
2213
|
+
emitCandidates(emitter, key, name, writes);
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2216
|
+
add(emitter, writes.length === 1 ? "binds" : "endsHolding", key, settled);
|
|
2217
|
+
if (atModule) {
|
|
2218
|
+
add(emitter, "exportsAs", emitter.filePath, name, settled);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
function emitCandidates(emitter, key, name, writes) {
|
|
2223
|
+
for (const write of writes) {
|
|
2224
|
+
if (write.value === null) {
|
|
2225
|
+
if (write.given === null) {
|
|
2226
|
+
add(emitter, "writesUnstated", key);
|
|
2227
|
+
}
|
|
2228
|
+
continue;
|
|
2229
|
+
}
|
|
2230
|
+
if (!describeWrite(emitter, write, name).narrowsName) {
|
|
2231
|
+
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
function emitValueFacts(db, filePath, root) {
|
|
2236
|
+
const emitter = { db, filePath, enclosing: null };
|
|
2237
|
+
emitNestedDefinitions(emitter, root);
|
|
2238
|
+
emitScopeWrites(emitter, readScope(root, []), true);
|
|
1831
2239
|
emitExpressionFacts(emitter, root);
|
|
1832
2240
|
}
|
|
1833
2241
|
|
|
@@ -1940,7 +2348,7 @@ function pythonLowering(options) {
|
|
|
1940
2348
|
return {
|
|
1941
2349
|
idOf: (node) => node.id,
|
|
1942
2350
|
expression: (node) => expressionOf(node, originOf2),
|
|
1943
|
-
statement:
|
|
2351
|
+
statement: statementOf2,
|
|
1944
2352
|
siteOf,
|
|
1945
2353
|
functionOf,
|
|
1946
2354
|
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
@@ -2204,7 +2612,7 @@ var STATEMENT_TYPES = {
|
|
|
2204
2612
|
body: blockStatements2(field(node, "body"))
|
|
2205
2613
|
})
|
|
2206
2614
|
};
|
|
2207
|
-
function
|
|
2615
|
+
function statementOf2(node) {
|
|
2208
2616
|
const lower = STATEMENT_TYPES[node.type];
|
|
2209
2617
|
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2210
2618
|
}
|
|
@@ -3274,9 +3682,13 @@ function constructionThroughFacts(name, scope, scan) {
|
|
|
3274
3682
|
if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
|
|
3275
3683
|
return null;
|
|
3276
3684
|
}
|
|
3277
|
-
const
|
|
3278
|
-
|
|
3279
|
-
|
|
3685
|
+
const nameKey2 = nameKeyIn(
|
|
3686
|
+
scan.bound.file,
|
|
3687
|
+
scope.kind === "function" ? scope.node : enclosingFunction(scope.node),
|
|
3688
|
+
name
|
|
3689
|
+
);
|
|
3690
|
+
const constructionKey2 = subjectConstructions(facts, [nameKey2]).get(
|
|
3691
|
+
nameKey2
|
|
3280
3692
|
)?.constructionKey;
|
|
3281
3693
|
return constructionKey2 === void 0 ? null : scan.index.byValueKey.get(constructionKey2) ?? null;
|
|
3282
3694
|
}
|
|
@@ -3347,11 +3759,15 @@ var WALK_DESCENTS = {
|
|
|
3347
3759
|
scan,
|
|
3348
3760
|
visit
|
|
3349
3761
|
);
|
|
3350
|
-
}
|
|
3762
|
+
},
|
|
3763
|
+
if_statement: descendInPlace,
|
|
3764
|
+
try_statement: descendInPlace,
|
|
3765
|
+
while_statement: descendInPlace,
|
|
3766
|
+
with_statement: descendInPlace,
|
|
3767
|
+
match_statement: descendInPlace
|
|
3351
3768
|
};
|
|
3352
|
-
function
|
|
3353
|
-
|
|
3354
|
-
return body === null ? [] : bodyStatements(body);
|
|
3769
|
+
function descendInPlace(stmt, position, scan, visit) {
|
|
3770
|
+
walkStatements(nestedStatements(stmt), position, scan, visit);
|
|
3355
3771
|
}
|
|
3356
3772
|
function walkStatements(statements, position, scan, visit) {
|
|
3357
3773
|
for (const stmt of statements) {
|
|
@@ -3363,11 +3779,11 @@ function walkStatements(statements, position, scan, visit) {
|
|
|
3363
3779
|
visit(stmt, position, scan);
|
|
3364
3780
|
}
|
|
3365
3781
|
}
|
|
3366
|
-
function
|
|
3782
|
+
function targetNames2(node) {
|
|
3367
3783
|
if (node.type === "identifier") {
|
|
3368
3784
|
return [node.text];
|
|
3369
3785
|
}
|
|
3370
|
-
return bodyStatements(node).flatMap(
|
|
3786
|
+
return bodyStatements(node).flatMap(targetNames2);
|
|
3371
3787
|
}
|
|
3372
3788
|
function sequenceElementNames(node) {
|
|
3373
3789
|
if (node === null || node.type !== "list" && node.type !== "tuple") {
|
|
@@ -3404,7 +3820,7 @@ function loopBindingsOf(stmt, position, scan) {
|
|
|
3404
3820
|
});
|
|
3405
3821
|
return bindings;
|
|
3406
3822
|
}
|
|
3407
|
-
for (const name of
|
|
3823
|
+
for (const name of targetNames2(left)) {
|
|
3408
3824
|
bindings.set(name, { kind: "unenumerable", location });
|
|
3409
3825
|
}
|
|
3410
3826
|
return bindings;
|
|
@@ -4432,7 +4848,7 @@ function rawSqlEffects(calls, options) {
|
|
|
4432
4848
|
binding: storageBinding({
|
|
4433
4849
|
recognition: `python-${pattern.module}`,
|
|
4434
4850
|
storageSystem: pattern.storageSystem,
|
|
4435
|
-
scope:
|
|
4851
|
+
scope: "default",
|
|
4436
4852
|
container: access.table
|
|
4437
4853
|
}),
|
|
4438
4854
|
callee: field(call, "function")?.text ?? "",
|
|
@@ -4576,16 +4992,23 @@ function typeNameOf(annotation) {
|
|
|
4576
4992
|
if (quoted !== null) {
|
|
4577
4993
|
return quoted;
|
|
4578
4994
|
}
|
|
4995
|
+
if (annotation.type === "binary_operator") {
|
|
4996
|
+
const named = [field(annotation, "left"), field(annotation, "right")].find(
|
|
4997
|
+
(side) => side !== null && side.type !== "none"
|
|
4998
|
+
);
|
|
4999
|
+
return named === void 0 || named === null ? null : typeNameOf(named);
|
|
5000
|
+
}
|
|
4579
5001
|
if (annotation.type === "generic_type") {
|
|
4580
5002
|
const outer = annotation.namedChildren[0];
|
|
4581
5003
|
const first = genericTypeArgs(annotation)[0];
|
|
4582
5004
|
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
4583
5005
|
return typeNameOf(first);
|
|
4584
5006
|
}
|
|
5007
|
+
return outer?.type === "identifier" ? outer.text : null;
|
|
4585
5008
|
}
|
|
4586
5009
|
return null;
|
|
4587
5010
|
}
|
|
4588
|
-
function typeGivenBy(statement, name) {
|
|
5011
|
+
function typeGivenBy(statement, name, options) {
|
|
4589
5012
|
if (statement.type === "assignment") {
|
|
4590
5013
|
if (field(statement, "left")?.text !== name) {
|
|
4591
5014
|
return null;
|
|
@@ -4595,8 +5018,7 @@ function typeGivenBy(statement, name) {
|
|
|
4595
5018
|
return typeNameOf(annotation);
|
|
4596
5019
|
}
|
|
4597
5020
|
const right = field(statement, "right");
|
|
4598
|
-
|
|
4599
|
-
return callee?.type === "identifier" ? callee.text : null;
|
|
5021
|
+
return right?.type === "call" ? builtType(right, options) : null;
|
|
4600
5022
|
}
|
|
4601
5023
|
if (statement.type === "as_pattern") {
|
|
4602
5024
|
const alias = field(statement, "alias");
|
|
@@ -4606,7 +5028,34 @@ function typeGivenBy(statement, name) {
|
|
|
4606
5028
|
}
|
|
4607
5029
|
return null;
|
|
4608
5030
|
}
|
|
4609
|
-
function
|
|
5031
|
+
function builtType(call, options) {
|
|
5032
|
+
const root = rootOf(call);
|
|
5033
|
+
const callee = field(root, "function");
|
|
5034
|
+
if (callee?.type === "identifier") {
|
|
5035
|
+
return isClassName(callee.text) ? callee.text : resolvedReturnType(options, callee);
|
|
5036
|
+
}
|
|
5037
|
+
const object = callee?.type === "attribute" ? field(callee, "object") : null;
|
|
5038
|
+
if (object?.type === "identifier" && isClassName(object.text)) {
|
|
5039
|
+
return object.text;
|
|
5040
|
+
}
|
|
5041
|
+
const args = field(root, "arguments");
|
|
5042
|
+
const first = (args === null ? [] : children(args))[0];
|
|
5043
|
+
if (first?.type === "identifier" && isClassName(first.text)) {
|
|
5044
|
+
return first.text;
|
|
5045
|
+
}
|
|
5046
|
+
return callee === null ? null : resolvedReturnType(options, callee);
|
|
5047
|
+
}
|
|
5048
|
+
function resolvedReturnType(options, callee) {
|
|
5049
|
+
const settled = settledCallee(options, callee);
|
|
5050
|
+
return settled === void 0 ? null : returnTypeName(options.definitionAt(settled));
|
|
5051
|
+
}
|
|
5052
|
+
function settledCallee(options, callee) {
|
|
5053
|
+
const key = readKey(options.filePath, callee, enclosingFunction(callee));
|
|
5054
|
+
resolveCalls(options.facts, [key]);
|
|
5055
|
+
const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
5056
|
+
return resolved.length === 1 ? resolved[0] : void 0;
|
|
5057
|
+
}
|
|
5058
|
+
function declaredTypeName(name, from, options) {
|
|
4610
5059
|
const fn = enclosingFunction(from);
|
|
4611
5060
|
if (fn === null) {
|
|
4612
5061
|
return null;
|
|
@@ -4619,7 +5068,7 @@ function declaredTypeName(name, from) {
|
|
|
4619
5068
|
}
|
|
4620
5069
|
}
|
|
4621
5070
|
const visit = (node) => {
|
|
4622
|
-
const given = typeGivenBy(node, name);
|
|
5071
|
+
const given = typeGivenBy(node, name, options);
|
|
4623
5072
|
if (given !== null) {
|
|
4624
5073
|
return given;
|
|
4625
5074
|
}
|
|
@@ -4648,16 +5097,27 @@ function typedReceiverPattern(options, chain) {
|
|
|
4648
5097
|
if (receiver === null) {
|
|
4649
5098
|
return void 0;
|
|
4650
5099
|
}
|
|
4651
|
-
const typeName = declaredTypeName(receiver, chain.root);
|
|
5100
|
+
const typeName = declaredTypeName(receiver, chain.root, options);
|
|
4652
5101
|
if (typeName === null) {
|
|
4653
5102
|
return void 0;
|
|
4654
5103
|
}
|
|
4655
|
-
const
|
|
4656
|
-
|
|
4657
|
-
|
|
5104
|
+
const typeKey = `${options.filePath}#${typeName}`;
|
|
5105
|
+
const imported = options.facts.facts("pyImportedName").find((row) => String(row[0]) === typeKey);
|
|
5106
|
+
const direct = imported === void 0 ? void 0 : queryTypePattern(options, String(imported[1]), String(imported[2]));
|
|
5107
|
+
if (direct !== void 0) {
|
|
5108
|
+
return direct;
|
|
5109
|
+
}
|
|
5110
|
+
for (const origin of originsOf(options.facts, typeKey)) {
|
|
5111
|
+
const pattern = queryTypePattern(options, origin.module, origin.name);
|
|
5112
|
+
if (pattern !== void 0) {
|
|
5113
|
+
return pattern;
|
|
5114
|
+
}
|
|
4658
5115
|
}
|
|
5116
|
+
return void 0;
|
|
5117
|
+
}
|
|
5118
|
+
function queryTypePattern(options, module, name) {
|
|
4659
5119
|
return options.patterns.find(
|
|
4660
|
-
(pattern) =>
|
|
5120
|
+
(pattern) => module === pattern.module && pattern.queryTypes.includes(name)
|
|
4661
5121
|
);
|
|
4662
5122
|
}
|
|
4663
5123
|
function fieldsOf(chain, valueMethods) {
|
|
@@ -4703,16 +5163,80 @@ function keywordNames(call) {
|
|
|
4703
5163
|
}
|
|
4704
5164
|
}
|
|
4705
5165
|
}
|
|
4706
|
-
return picked;
|
|
5166
|
+
return picked;
|
|
5167
|
+
}
|
|
5168
|
+
function selectorOf(chain, valueMethods) {
|
|
5169
|
+
return [
|
|
5170
|
+
...new Set(
|
|
5171
|
+
laterCalls(chain).filter((call) => !valueMethods.includes(methodNameOf(call))).flatMap(keywordNames)
|
|
5172
|
+
)
|
|
5173
|
+
];
|
|
5174
|
+
}
|
|
5175
|
+
var TABLE_METHODS = ["select_from", "join"];
|
|
5176
|
+
function modelOf(chain, options) {
|
|
5177
|
+
const fromRoot = modelArgument(chain.root, chain.root, options);
|
|
5178
|
+
if (fromRoot !== null) {
|
|
5179
|
+
return fromRoot;
|
|
5180
|
+
}
|
|
5181
|
+
for (const call of laterCalls(chain)) {
|
|
5182
|
+
if (TABLE_METHODS.includes(methodNameOf(call))) {
|
|
5183
|
+
const named = modelArgument(call, chain.root, options);
|
|
5184
|
+
if (named !== null) {
|
|
5185
|
+
return named;
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
return isClassName(chain.subject) ? chain.subject : null;
|
|
5190
|
+
}
|
|
5191
|
+
function modelArgument(call, from, options) {
|
|
5192
|
+
const args = field(call, "arguments");
|
|
5193
|
+
const first = (args === null ? [] : children(args)).find(
|
|
5194
|
+
(argument) => argument.type !== "keyword_argument"
|
|
5195
|
+
);
|
|
5196
|
+
return first === void 0 ? null : modelNamed(first, from, options);
|
|
5197
|
+
}
|
|
5198
|
+
function modelNamed(node, from, options) {
|
|
5199
|
+
if (node.type === "identifier") {
|
|
5200
|
+
if (isClassName(node.text)) {
|
|
5201
|
+
return node.text;
|
|
5202
|
+
}
|
|
5203
|
+
const declared = declaredTypeName(node.text, from, options);
|
|
5204
|
+
return declared === null ? null : classBehind(declared, options);
|
|
5205
|
+
}
|
|
5206
|
+
if (node.type === "attribute") {
|
|
5207
|
+
const property = field(node, "attribute");
|
|
5208
|
+
if (property !== null && isClassName(property.text)) {
|
|
5209
|
+
return property.text;
|
|
5210
|
+
}
|
|
5211
|
+
const object = field(node, "object");
|
|
5212
|
+
return object === null ? null : modelNamed(object, from, options);
|
|
5213
|
+
}
|
|
5214
|
+
if (node.type === "call") {
|
|
5215
|
+
const callee = field(node, "function");
|
|
5216
|
+
const built = callee === null ? null : modelNamed(callee, from, options);
|
|
5217
|
+
return built ?? modelArgument(node, from, options);
|
|
5218
|
+
}
|
|
5219
|
+
return null;
|
|
4707
5220
|
}
|
|
4708
|
-
function
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
)
|
|
4713
|
-
|
|
5221
|
+
function classBehind(typeName, options) {
|
|
5222
|
+
const key = `${options.filePath}#${typeName}`;
|
|
5223
|
+
originsOf(options.facts, key);
|
|
5224
|
+
const reached = new Set(
|
|
5225
|
+
options.facts.facts("wantedComesTo").filter((row) => String(row[0]) === key).map((row) => String(row[1]))
|
|
5226
|
+
);
|
|
5227
|
+
const classes = new Set(
|
|
5228
|
+
options.facts.facts("objectValue").map((row) => String(row[0])).filter((value) => reached.has(value))
|
|
5229
|
+
);
|
|
5230
|
+
const exported = options.facts.facts("exportsAs").find((row) => classes.has(String(row[2])));
|
|
5231
|
+
if (exported !== void 0) {
|
|
5232
|
+
return String(exported[1]);
|
|
5233
|
+
}
|
|
5234
|
+
return isClassName(typeName) ? typeName : null;
|
|
4714
5235
|
}
|
|
4715
|
-
function
|
|
5236
|
+
function isClassName(name) {
|
|
5237
|
+
return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
|
|
5238
|
+
}
|
|
5239
|
+
function effectFor(pattern, chain, operation, options) {
|
|
4716
5240
|
const valueMethods = pattern.valueMethods ?? [];
|
|
4717
5241
|
const picked = selectorOf(chain, valueMethods);
|
|
4718
5242
|
return {
|
|
@@ -4720,8 +5244,8 @@ function effectFor(pattern, chain, operation) {
|
|
|
4720
5244
|
binding: storageBinding2({
|
|
4721
5245
|
recognition: "python-storage",
|
|
4722
5246
|
storageSystem: pattern.storageSystem,
|
|
4723
|
-
scope:
|
|
4724
|
-
container: chain
|
|
5247
|
+
scope: "default",
|
|
5248
|
+
container: modelOf(chain, options)
|
|
4725
5249
|
}),
|
|
4726
5250
|
callee: chain.last.text,
|
|
4727
5251
|
interaction: {
|
|
@@ -4738,23 +5262,14 @@ function fileOf(key) {
|
|
|
4738
5262
|
return at === -1 ? key : key.slice(0, at);
|
|
4739
5263
|
}
|
|
4740
5264
|
function returnTypeName(node) {
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
}
|
|
4744
|
-
const annotation = field(node, "return_type");
|
|
4745
|
-
if (annotation === null) {
|
|
4746
|
-
return null;
|
|
4747
|
-
}
|
|
4748
|
-
return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
|
|
5265
|
+
const annotation = node === void 0 ? null : field(node, "return_type");
|
|
5266
|
+
return annotation === null ? null : typeNameOf(annotation);
|
|
4749
5267
|
}
|
|
4750
5268
|
function storageCallees(calls, filePath, couldMatch) {
|
|
4751
5269
|
return chainsIn(calls).filter((chain) => couldMatch.has(methodName(chain.root))).map((chain) => field(chain.root, "function")).filter((callee) => callee !== null).map((callee) => readKey(filePath, callee, enclosingFunction(callee)));
|
|
4752
5270
|
}
|
|
4753
5271
|
function resolvedMethodPattern(options, callee) {
|
|
4754
|
-
const
|
|
4755
|
-
(row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
|
|
4756
|
-
).map((row) => String(row[1]));
|
|
4757
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
5272
|
+
const settled = settledCallee(options, callee);
|
|
4758
5273
|
if (settled === void 0) {
|
|
4759
5274
|
return void 0;
|
|
4760
5275
|
}
|
|
@@ -4781,14 +5296,9 @@ function matchedChains(calls, options) {
|
|
|
4781
5296
|
if (chains.length === 0) {
|
|
4782
5297
|
return [];
|
|
4783
5298
|
}
|
|
4784
|
-
const alreadyAsked = new Set(
|
|
4785
|
-
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
4786
|
-
);
|
|
4787
5299
|
resolveCalls(
|
|
4788
5300
|
options.facts,
|
|
4789
|
-
storageCallees(calls, options.filePath, options.couldMatch)
|
|
4790
|
-
(key) => !alreadyAsked.has(key)
|
|
4791
|
-
)
|
|
5301
|
+
storageCallees(calls, options.filePath, options.couldMatch)
|
|
4792
5302
|
);
|
|
4793
5303
|
const matched = [];
|
|
4794
5304
|
for (const chain of chains) {
|
|
@@ -4831,7 +5341,7 @@ function storageCallIds(calls, options) {
|
|
|
4831
5341
|
}
|
|
4832
5342
|
function storageEffects(calls, options) {
|
|
4833
5343
|
return matchedChains(calls, options).flatMap(
|
|
4834
|
-
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
|
|
5344
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation, options)]
|
|
4835
5345
|
);
|
|
4836
5346
|
}
|
|
4837
5347
|
|
|
@@ -5127,6 +5637,7 @@ function classRouteUnits(pattern, pack, classification, classNode, module, optio
|
|
|
5127
5637
|
pattern
|
|
5128
5638
|
),
|
|
5129
5639
|
storage: options.storage,
|
|
5640
|
+
importedDefinition: options.importedDefinition,
|
|
5130
5641
|
wrappers: wrappersAround(
|
|
5131
5642
|
pattern,
|
|
5132
5643
|
pack,
|
|
@@ -5456,7 +5967,10 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
5456
5967
|
if (functionName === void 0) {
|
|
5457
5968
|
return [];
|
|
5458
5969
|
}
|
|
5459
|
-
const ctx = createAnnotationContext(
|
|
5970
|
+
const ctx = createAnnotationContext(
|
|
5971
|
+
module.scopeFor,
|
|
5972
|
+
options.importedDefinition ?? null
|
|
5973
|
+
);
|
|
5460
5974
|
return readRoutePaths(
|
|
5461
5975
|
pattern,
|
|
5462
5976
|
readPathArgument(classification, options),
|
|
@@ -5495,6 +6009,7 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
5495
6009
|
),
|
|
5496
6010
|
definitionsCtx: ctx,
|
|
5497
6011
|
storage: options.storage,
|
|
6012
|
+
importedDefinition: options.importedDefinition,
|
|
5498
6013
|
wrappers: wrappersAround(
|
|
5499
6014
|
pattern,
|
|
5500
6015
|
pack,
|
|
@@ -5592,7 +6107,10 @@ function buildRouteUnit(options) {
|
|
|
5592
6107
|
storage: storageLookup
|
|
5593
6108
|
} = options;
|
|
5594
6109
|
const template = valueToReadFurtherFrom(routePath);
|
|
5595
|
-
const ctx = definitionsCtx ?? createAnnotationContext(
|
|
6110
|
+
const ctx = definitionsCtx ?? createAnnotationContext(
|
|
6111
|
+
module.scopeFor,
|
|
6112
|
+
options.importedDefinition ?? null
|
|
6113
|
+
);
|
|
5596
6114
|
const parameters = readParameters(
|
|
5597
6115
|
definitionNode,
|
|
5598
6116
|
enclosingScope,
|
|
@@ -5673,7 +6191,7 @@ function buildRouteUnit(options) {
|
|
|
5673
6191
|
declaredContract: null,
|
|
5674
6192
|
readings: [
|
|
5675
6193
|
routePath,
|
|
5676
|
-
...unreadRoleReadings(parameters, rangeOf(definitionNode)),
|
|
6194
|
+
...template === null ? unreadRoleReadings(parameters, rangeOf(definitionNode)) : [],
|
|
5677
6195
|
...responseShape.passedOver
|
|
5678
6196
|
],
|
|
5679
6197
|
...collectedDefinitions(ctx) !== null ? { definitions: collectedDefinitions(ctx) } : {},
|
|
@@ -5768,7 +6286,7 @@ function readParameter(param, scope, ctx, pathParamNames, requestBodyFromAnnotat
|
|
|
5768
6286
|
}
|
|
5769
6287
|
const { name, typeNode } = info;
|
|
5770
6288
|
const shape = typeNode !== null ? annotationToShape(typeNode, scope, ctx) : null;
|
|
5771
|
-
const role = isInjectedParameter(param, injectedCallees) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
|
|
6289
|
+
const role = isInjectedParameter(param, injectedCallees, scope, ctx) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
|
|
5772
6290
|
return {
|
|
5773
6291
|
name,
|
|
5774
6292
|
position,
|
|
@@ -5802,7 +6320,7 @@ function calleeName2(call) {
|
|
|
5802
6320
|
}
|
|
5803
6321
|
return null;
|
|
5804
6322
|
}
|
|
5805
|
-
function isInjectedParameter(param, injectedCallees) {
|
|
6323
|
+
function isInjectedParameter(param, injectedCallees, scope, ctx) {
|
|
5806
6324
|
if (injectedCallees.size === 0) {
|
|
5807
6325
|
return false;
|
|
5808
6326
|
}
|
|
@@ -5816,25 +6334,33 @@ function isInjectedParameter(param, injectedCallees) {
|
|
|
5816
6334
|
if (callsAnInjector(field(param, "value"))) {
|
|
5817
6335
|
return true;
|
|
5818
6336
|
}
|
|
5819
|
-
const
|
|
6337
|
+
const followed = /* @__PURE__ */ new Set();
|
|
6338
|
+
const containsInjectorCall = (node, nodeScope, depth) => {
|
|
5820
6339
|
if (node === null || depth > MAX_ANNOTATION_DEPTH) {
|
|
5821
6340
|
return false;
|
|
5822
6341
|
}
|
|
5823
6342
|
if (callsAnInjector(node)) {
|
|
5824
6343
|
return true;
|
|
5825
6344
|
}
|
|
6345
|
+
if (node.type === "identifier" && !followed.has(node.id)) {
|
|
6346
|
+
followed.add(node.id);
|
|
6347
|
+
const alias = aliasValueOf(node.text, nodeScope, ctx);
|
|
6348
|
+
if (alias !== null && containsInjectorCall(alias.node, alias.scope, 0)) {
|
|
6349
|
+
return true;
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
5826
6352
|
return node.namedChildren.some(
|
|
5827
|
-
(child) => containsInjectorCall(child, depth + 1)
|
|
6353
|
+
(child) => containsInjectorCall(child, nodeScope, depth + 1)
|
|
5828
6354
|
);
|
|
5829
6355
|
};
|
|
5830
|
-
return containsInjectorCall(field(param, "type"), 0);
|
|
6356
|
+
return containsInjectorCall(field(param, "type"), scope, 0);
|
|
5831
6357
|
}
|
|
5832
6358
|
|
|
5833
6359
|
// src/facts/why.ts
|
|
5834
6360
|
import fs4 from "fs";
|
|
5835
6361
|
import path4 from "path";
|
|
5836
6362
|
import { Database as Database3 } from "@suss/datalog";
|
|
5837
|
-
import { explainResolvedKey, RESOLUTION_RULES
|
|
6363
|
+
import { explainResolvedKey, RESOLUTION_RULES } from "@suss/resolution";
|
|
5838
6364
|
|
|
5839
6365
|
// src/facts.ts
|
|
5840
6366
|
function unitKey(filePath, range, name) {
|
|
@@ -5852,7 +6378,7 @@ function emitModuleImportFacts(db, filePath, module, resolverOptions) {
|
|
|
5852
6378
|
if (binding.kind !== "import" && binding.kind !== "importFrom") {
|
|
5853
6379
|
continue;
|
|
5854
6380
|
}
|
|
5855
|
-
const
|
|
6381
|
+
const nameKey2 = `${filePath}#${localName}`;
|
|
5856
6382
|
const moduleText = importedModuleText(
|
|
5857
6383
|
binding.module,
|
|
5858
6384
|
binding.relativeLevel
|
|
@@ -5868,16 +6394,16 @@ function emitModuleImportFacts(db, filePath, module, resolverOptions) {
|
|
|
5868
6394
|
resolution.status === "resolved" ? "resolved" : resolution.reason
|
|
5869
6395
|
]);
|
|
5870
6396
|
const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
|
|
5871
|
-
db.add("pyImportedName", [
|
|
6397
|
+
db.add("pyImportedName", [nameKey2, moduleText, importedName]);
|
|
5872
6398
|
if (scope.kind === "module") {
|
|
5873
|
-
db.add("exportsAs", [filePath, localName,
|
|
6399
|
+
db.add("exportsAs", [filePath, localName, nameKey2]);
|
|
5874
6400
|
}
|
|
5875
6401
|
if (resolution.status === "resolved") {
|
|
5876
6402
|
db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
|
|
5877
|
-
db.add("imports", [
|
|
6403
|
+
db.add("imports", [nameKey2, resolution.file, importedName]);
|
|
5878
6404
|
continue;
|
|
5879
6405
|
}
|
|
5880
|
-
db.add("imports", [
|
|
6406
|
+
db.add("imports", [nameKey2, moduleText, importedName]);
|
|
5881
6407
|
}
|
|
5882
6408
|
}
|
|
5883
6409
|
for (const openModule of module.openImports) {
|
|
@@ -5985,6 +6511,58 @@ function buildPythonExtractionReport(args) {
|
|
|
5985
6511
|
});
|
|
5986
6512
|
}
|
|
5987
6513
|
|
|
6514
|
+
// src/importedDefinitions.ts
|
|
6515
|
+
function importedDefinitionLookup(db, files) {
|
|
6516
|
+
const fileOfModuleScope = /* @__PURE__ */ new Map();
|
|
6517
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
6518
|
+
for (const file of files) {
|
|
6519
|
+
fileOfModuleScope.set(file.module.moduleScope.node.id, file.file);
|
|
6520
|
+
byFile.set(file.file, file);
|
|
6521
|
+
}
|
|
6522
|
+
const found = /* @__PURE__ */ new Map();
|
|
6523
|
+
return (scope, name) => {
|
|
6524
|
+
const fromFile = fileOfModuleScope.get(moduleScopeOf(scope).node.id);
|
|
6525
|
+
if (fromFile === void 0) {
|
|
6526
|
+
return null;
|
|
6527
|
+
}
|
|
6528
|
+
const key = `${fromFile}#${name}`;
|
|
6529
|
+
const known = found.get(key);
|
|
6530
|
+
if (known !== void 0) {
|
|
6531
|
+
return known;
|
|
6532
|
+
}
|
|
6533
|
+
const definition = definitionAmong(originsOf(db, key), byFile);
|
|
6534
|
+
found.set(key, definition);
|
|
6535
|
+
return definition;
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6538
|
+
function definitionAmong(origins, byFile) {
|
|
6539
|
+
for (const origin of origins) {
|
|
6540
|
+
const file = byFile.get(origin.module);
|
|
6541
|
+
const binding = file?.module.moduleScope.bindings.get(origin.name);
|
|
6542
|
+
if (file === void 0 || binding === void 0) {
|
|
6543
|
+
continue;
|
|
6544
|
+
}
|
|
6545
|
+
const node = definedNodeOf(binding);
|
|
6546
|
+
if (node !== null) {
|
|
6547
|
+
return {
|
|
6548
|
+
node,
|
|
6549
|
+
moduleScope: file.module.moduleScope,
|
|
6550
|
+
scopeFor: file.module.scopeFor
|
|
6551
|
+
};
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
return null;
|
|
6555
|
+
}
|
|
6556
|
+
function definedNodeOf(binding) {
|
|
6557
|
+
if (binding.kind === "classDef") {
|
|
6558
|
+
return binding.node;
|
|
6559
|
+
}
|
|
6560
|
+
if (binding.kind === "assignment") {
|
|
6561
|
+
return binding.value;
|
|
6562
|
+
}
|
|
6563
|
+
return null;
|
|
6564
|
+
}
|
|
6565
|
+
|
|
5988
6566
|
// src/reach/closure.ts
|
|
5989
6567
|
import {
|
|
5990
6568
|
functionCallBinding,
|
|
@@ -5996,7 +6574,7 @@ import {
|
|
|
5996
6574
|
unfollowedCallGap,
|
|
5997
6575
|
worthRecording
|
|
5998
6576
|
} from "@suss/behavioral-ir";
|
|
5999
|
-
import { Database, evaluate
|
|
6577
|
+
import { Database, evaluate, lit, rule, variable as v } from "@suss/datalog";
|
|
6000
6578
|
import {
|
|
6001
6579
|
assembleSummary,
|
|
6002
6580
|
SKIP_CHILDREN as SKIP_CHILDREN2,
|
|
@@ -6004,248 +6582,227 @@ import {
|
|
|
6004
6582
|
} from "@suss/extractor";
|
|
6005
6583
|
|
|
6006
6584
|
// src/reach/resolveCallee.ts
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6585
|
+
import {
|
|
6586
|
+
calleeOutcomeOf,
|
|
6587
|
+
calleeOutcomes,
|
|
6588
|
+
writtenSourcesOf
|
|
6589
|
+
} from "@suss/resolution";
|
|
6590
|
+
var STOP_FOR = {
|
|
6591
|
+
severalSources: "multipleSources",
|
|
6592
|
+
outsideRun: "outsideRun",
|
|
6593
|
+
unsettled: "unsettledValue",
|
|
6594
|
+
undeclared: "noDeclaration"
|
|
6011
6595
|
};
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6596
|
+
var NO_DECLARATION = {
|
|
6597
|
+
kind: "stopped",
|
|
6598
|
+
reason: "noDeclaration"
|
|
6599
|
+
};
|
|
6600
|
+
function calleeSpellings(calls, ctx) {
|
|
6601
|
+
const spellingOf = /* @__PURE__ */ new Map();
|
|
6602
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6603
|
+
for (const { call, site } of calls) {
|
|
6604
|
+
const spelling = spellingFor(call, site, ctx);
|
|
6605
|
+
spellingOf.set(call.id, spelling);
|
|
6606
|
+
if (spelling.kind === "key") {
|
|
6607
|
+
keys.add(spelling.key);
|
|
6608
|
+
}
|
|
6016
6609
|
}
|
|
6017
|
-
return
|
|
6610
|
+
return { spellingOf, outcomes: calleeOutcomes(ctx.facts, [...keys]) };
|
|
6018
6611
|
}
|
|
6019
|
-
function
|
|
6020
|
-
|
|
6021
|
-
|
|
6612
|
+
function resolveCallee2(call, site, ctx, read) {
|
|
6613
|
+
const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site, ctx);
|
|
6614
|
+
if (spelling.kind === "stopped") {
|
|
6615
|
+
return spelling;
|
|
6022
6616
|
}
|
|
6023
|
-
const
|
|
6024
|
-
|
|
6617
|
+
const outcome = read?.outcomes.get(spelling.key) ?? calleeOutcomeOf(ctx.facts, spelling.key);
|
|
6618
|
+
return asCallee(outcome, site.owner, ctx);
|
|
6619
|
+
}
|
|
6620
|
+
function functionNamed(nameKey2, ctx) {
|
|
6621
|
+
if (assignedElsewhere(ctx.facts, nameKey2)) {
|
|
6025
6622
|
return null;
|
|
6026
6623
|
}
|
|
6027
|
-
const
|
|
6028
|
-
return
|
|
6624
|
+
const outcome = calleeOutcomeOf(ctx.facts, nameKey2);
|
|
6625
|
+
return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
|
|
6029
6626
|
}
|
|
6030
|
-
function
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
);
|
|
6039
|
-
}
|
|
6040
|
-
if (resolved.kind === "stop") {
|
|
6041
|
-
return { kind: "stopped", reason: resolved.reason };
|
|
6042
|
-
}
|
|
6043
|
-
if (resolved.kind === "package") {
|
|
6044
|
-
return { kind: "stopped", reason: "outsideRun" };
|
|
6627
|
+
function assignedElsewhere(facts, nameKey2) {
|
|
6628
|
+
return writtenSourcesOf(facts, nameKey2).some(
|
|
6629
|
+
(source) => !facts.has("func", [source])
|
|
6630
|
+
);
|
|
6631
|
+
}
|
|
6632
|
+
function readThrough(node) {
|
|
6633
|
+
if (node?.type !== "parenthesized_expression") {
|
|
6634
|
+
return node;
|
|
6045
6635
|
}
|
|
6046
|
-
return
|
|
6636
|
+
return readThrough(children(node)[0] ?? null);
|
|
6047
6637
|
}
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
const attribute = field(node, "attribute");
|
|
6053
|
-
if (object === null || attribute === null) {
|
|
6054
|
-
return stop("noDeclaration");
|
|
6055
|
-
}
|
|
6056
|
-
return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
|
|
6057
|
-
},
|
|
6058
|
-
// `Service().run()` runs a method on whatever the constructor built.
|
|
6059
|
-
call: (node, site, ctx) => {
|
|
6060
|
-
const callee = field(node, "function");
|
|
6061
|
-
if (callee === null) {
|
|
6062
|
-
return stop("noDeclaration");
|
|
6063
|
-
}
|
|
6064
|
-
const built = resolveExpression(callee, site, ctx);
|
|
6065
|
-
return built.kind === "class" ? built : stop("noDeclaration");
|
|
6066
|
-
},
|
|
6067
|
-
parenthesized_expression: (node, site, ctx) => {
|
|
6068
|
-
const inner = node.namedChildren[0];
|
|
6069
|
-
return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
|
|
6638
|
+
function spellingFor(call, site, ctx) {
|
|
6639
|
+
const callee = readThrough(field(call, "function"));
|
|
6640
|
+
if (callee === null) {
|
|
6641
|
+
return { kind: "stopped", reason: "noDeclaration" };
|
|
6070
6642
|
}
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6643
|
+
return throughModules(callee, site, ctx) ?? {
|
|
6644
|
+
kind: "key",
|
|
6645
|
+
key: readKey(site.file.file, callee, enclosingFunction(callee))
|
|
6646
|
+
};
|
|
6075
6647
|
}
|
|
6076
|
-
function
|
|
6077
|
-
if (
|
|
6078
|
-
return
|
|
6648
|
+
function asCallee(outcome, owner, ctx) {
|
|
6649
|
+
if (outcome.kind === "function") {
|
|
6650
|
+
return functionCallee(outcome.key, ctx);
|
|
6079
6651
|
}
|
|
6080
|
-
if (
|
|
6081
|
-
return
|
|
6652
|
+
if (outcome.kind === "object") {
|
|
6653
|
+
return constructorOf(outcome.key, ctx);
|
|
6082
6654
|
}
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6655
|
+
if (outcome.kind === "callerSupplied") {
|
|
6656
|
+
return outcome.key.startsWith(`${owner}#`) ? { kind: "stopped", reason: "callerSupplied" } : { kind: "stopped", reason: "unsettledValue" };
|
|
6086
6657
|
}
|
|
6087
|
-
return
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6658
|
+
return {
|
|
6659
|
+
kind: "stopped",
|
|
6660
|
+
reason: STOP_FOR[outcome.kind] ?? "noDeclaration"
|
|
6661
|
+
};
|
|
6091
6662
|
}
|
|
6092
|
-
function
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
return stop("noDeclaration");
|
|
6663
|
+
function functionCallee(key, ctx) {
|
|
6664
|
+
if (ctx.definitions.get(key)?.type === "lambda") {
|
|
6665
|
+
return { kind: "stopped", reason: "unsettledValue" };
|
|
6096
6666
|
}
|
|
6097
|
-
|
|
6098
|
-
}
|
|
6099
|
-
var BINDING_RESOLVERS = {
|
|
6100
|
-
functionDef: (binding, file) => ({
|
|
6101
|
-
kind: "function",
|
|
6102
|
-
target: reachedFunction(file, binding.node)
|
|
6103
|
-
}),
|
|
6104
|
-
classDef: (binding, file) => ({ kind: "class", file, node: binding.node }),
|
|
6105
|
-
parameter: () => stop("callerSupplied"),
|
|
6106
|
-
// `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
|
|
6107
|
-
import: (binding, file, _scope, ctx) => moduleNamed(
|
|
6108
|
-
file,
|
|
6109
|
-
binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
|
|
6110
|
-
0,
|
|
6111
|
-
ctx
|
|
6112
|
-
),
|
|
6113
|
-
importFrom: (binding, file, _scope, ctx, visited) => memberOf(
|
|
6114
|
-
moduleNamed(file, binding.module, binding.relativeLevel, ctx),
|
|
6115
|
-
binding.importedName,
|
|
6116
|
-
ctx,
|
|
6117
|
-
visited
|
|
6118
|
-
),
|
|
6119
|
-
// `run = load` is an alias worth one hop; anything else is a value.
|
|
6120
|
-
assignment: (binding, file, scope, ctx, visited) => aliasedValue(binding.value, file, scope, ctx, visited),
|
|
6121
|
-
global: () => stop("unsettledValue"),
|
|
6122
|
-
nonlocal: () => stop("unsettledValue")
|
|
6123
|
-
};
|
|
6124
|
-
function resolveBinding(binding, file, scope, ctx, visited) {
|
|
6125
|
-
const resolver = BINDING_RESOLVERS[binding.kind];
|
|
6126
|
-
return resolver(binding, file, scope, ctx, visited);
|
|
6667
|
+
const target = functionAt(key, ctx);
|
|
6668
|
+
return target === null ? NO_DECLARATION : { kind: "followed", target };
|
|
6127
6669
|
}
|
|
6128
|
-
function
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
if (value.type === "identifier") {
|
|
6133
|
-
const binding = resolveName(scope, value.text);
|
|
6134
|
-
if (binding === null || binding.kind === "assignment") {
|
|
6135
|
-
return stop("unsettledValue");
|
|
6136
|
-
}
|
|
6137
|
-
return resolveBinding(binding, file, scope, ctx, visited);
|
|
6138
|
-
}
|
|
6139
|
-
if (value.type === "call") {
|
|
6140
|
-
const callee = field(value, "function");
|
|
6141
|
-
const built = callee === null ? stop("unsettledValue") : aliasedValue(callee, file, scope, ctx, visited);
|
|
6142
|
-
return built.kind === "class" ? built : stop("unsettledValue");
|
|
6670
|
+
function constructorOf(classKey, ctx) {
|
|
6671
|
+
const declared = ctx.facts.lookup("holdsProperty", 0, classKey).find((row) => String(row[1]) === "__init__");
|
|
6672
|
+
if (declared === void 0) {
|
|
6673
|
+
return NO_DECLARATION;
|
|
6143
6674
|
}
|
|
6144
|
-
|
|
6145
|
-
const object = field(value, "object");
|
|
6146
|
-
const attribute = field(value, "attribute");
|
|
6147
|
-
if (object === null || attribute === null) {
|
|
6148
|
-
return stop("unsettledValue");
|
|
6149
|
-
}
|
|
6150
|
-
return memberOf(
|
|
6151
|
-
aliasedValue(object, file, scope, ctx, visited),
|
|
6152
|
-
attribute.text,
|
|
6153
|
-
ctx
|
|
6154
|
-
);
|
|
6155
|
-
}
|
|
6156
|
-
return stop("unsettledValue");
|
|
6675
|
+
return functionCallee(String(declared[2]), ctx);
|
|
6157
6676
|
}
|
|
6158
|
-
function
|
|
6677
|
+
function functionAt(key, ctx) {
|
|
6678
|
+
const node = ctx.definitions.get(key);
|
|
6679
|
+
const file = ctx.filesByPath.get(key.slice(0, key.lastIndexOf(":")));
|
|
6680
|
+
if (node === void 0 || file === void 0) {
|
|
6681
|
+
return null;
|
|
6682
|
+
}
|
|
6159
6683
|
const name = field(node, "name")?.text ?? "<anon>";
|
|
6160
|
-
const
|
|
6684
|
+
const owner = ownerClassOf(node);
|
|
6685
|
+
const ownerName = owner === null ? void 0 : field(owner, "name")?.text;
|
|
6161
6686
|
return {
|
|
6162
6687
|
file,
|
|
6163
6688
|
node,
|
|
6164
6689
|
name,
|
|
6165
|
-
exportPath: ownerName ===
|
|
6690
|
+
exportPath: ownerName === void 0 ? [name] : [ownerName, name]
|
|
6166
6691
|
};
|
|
6167
6692
|
}
|
|
6168
|
-
function
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6693
|
+
function ownerClassOf(node) {
|
|
6694
|
+
for (let up = node.parent; up !== null; up = up.parent) {
|
|
6695
|
+
if (isFunction(up)) {
|
|
6696
|
+
return null;
|
|
6697
|
+
}
|
|
6698
|
+
if (up.type === "class_definition") {
|
|
6699
|
+
return up;
|
|
6700
|
+
}
|
|
6176
6701
|
}
|
|
6177
|
-
|
|
6178
|
-
return file === void 0 ? stop("outsideRun") : { kind: "module", file };
|
|
6702
|
+
return null;
|
|
6179
6703
|
}
|
|
6180
|
-
function
|
|
6181
|
-
if (
|
|
6182
|
-
return
|
|
6704
|
+
function throughModules(callee, site, ctx) {
|
|
6705
|
+
if (callee.type === "identifier") {
|
|
6706
|
+
return resolveName(site.scope, callee.text) === null ? openImportMember(site.file, callee.text, ctx, /* @__PURE__ */ new Set()) : null;
|
|
6183
6707
|
}
|
|
6184
|
-
if (
|
|
6185
|
-
return
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6708
|
+
if (callee.type !== "attribute") {
|
|
6709
|
+
return null;
|
|
6710
|
+
}
|
|
6711
|
+
const object = field(callee, "object");
|
|
6712
|
+
const attribute = field(callee, "attribute");
|
|
6713
|
+
if (object === null || attribute === null) {
|
|
6714
|
+
return null;
|
|
6191
6715
|
}
|
|
6192
|
-
|
|
6193
|
-
|
|
6716
|
+
const base = moduleAt(object, site, ctx);
|
|
6717
|
+
return base === null ? null : memberKey(base, attribute.text, ctx);
|
|
6718
|
+
}
|
|
6719
|
+
function openImportMember(file, name, ctx, visited) {
|
|
6720
|
+
if (visited.has(file.file)) {
|
|
6721
|
+
return null;
|
|
6194
6722
|
}
|
|
6195
|
-
|
|
6196
|
-
|
|
6723
|
+
visited.add(file.file);
|
|
6724
|
+
const found = /* @__PURE__ */ new Set();
|
|
6725
|
+
for (const spec of file.module.openImports) {
|
|
6726
|
+
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6727
|
+
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6728
|
+
if (opened === null || opened.kind !== "module") {
|
|
6729
|
+
continue;
|
|
6730
|
+
}
|
|
6731
|
+
const member = opened.file.module.moduleScope.bindings.has(name) ? { kind: "key", key: `${opened.file.file}#${name}` } : openImportMember(opened.file, name, ctx, visited);
|
|
6732
|
+
if (member?.kind === "key") {
|
|
6733
|
+
found.add(member.key);
|
|
6734
|
+
}
|
|
6197
6735
|
}
|
|
6198
|
-
|
|
6736
|
+
if (found.size > 1) {
|
|
6737
|
+
return { kind: "stopped", reason: "multipleSources" };
|
|
6738
|
+
}
|
|
6739
|
+
const only = [...found][0];
|
|
6740
|
+
return only === void 0 ? null : { kind: "key", key: only };
|
|
6199
6741
|
}
|
|
6200
|
-
function
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
if (binding === void 0) {
|
|
6204
|
-
return stop("noDeclaration");
|
|
6742
|
+
function moduleAt(node, site, ctx) {
|
|
6743
|
+
if (node.type === "identifier") {
|
|
6744
|
+
return importedModule(node.text, site, ctx);
|
|
6205
6745
|
}
|
|
6206
|
-
if (
|
|
6207
|
-
return
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6746
|
+
if (node.type !== "attribute") {
|
|
6747
|
+
return null;
|
|
6748
|
+
}
|
|
6749
|
+
const object = field(node, "object");
|
|
6750
|
+
const attribute = field(node, "attribute");
|
|
6751
|
+
if (object === null || attribute === null) {
|
|
6752
|
+
return null;
|
|
6211
6753
|
}
|
|
6212
|
-
|
|
6754
|
+
const base = moduleAt(object, site, ctx);
|
|
6755
|
+
return base === null ? null : submoduleOf(base, attribute.text, ctx);
|
|
6213
6756
|
}
|
|
6214
|
-
function
|
|
6215
|
-
const
|
|
6216
|
-
if (
|
|
6217
|
-
|
|
6757
|
+
function importedModule(name, site, ctx) {
|
|
6758
|
+
const binding = resolveName(site.scope, name);
|
|
6759
|
+
if (binding?.kind === "import") {
|
|
6760
|
+
const head = binding.module.split(".")[0];
|
|
6761
|
+
const dotted = binding.localName === head ? binding.localName : binding.module;
|
|
6762
|
+
return moduleNamed(site.file, dotted, 0, ctx);
|
|
6218
6763
|
}
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
if (binding !== void 0) {
|
|
6222
|
-
return resolveBinding(binding, file, file.module.moduleScope, ctx, visited);
|
|
6764
|
+
if (binding?.kind !== "importFrom") {
|
|
6765
|
+
return null;
|
|
6223
6766
|
}
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6767
|
+
const from = moduleNamed(
|
|
6768
|
+
site.file,
|
|
6769
|
+
binding.module,
|
|
6770
|
+
binding.relativeLevel,
|
|
6771
|
+
ctx
|
|
6772
|
+
);
|
|
6773
|
+
return from === null ? null : submoduleOf(from, binding.importedName, ctx);
|
|
6774
|
+
}
|
|
6775
|
+
function submoduleOf(base, name, ctx) {
|
|
6776
|
+
if (base.kind === "package") {
|
|
6777
|
+
return moduleNamed(
|
|
6778
|
+
base.from,
|
|
6779
|
+
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
6780
|
+
base.relativeLevel,
|
|
6781
|
+
ctx
|
|
6782
|
+
);
|
|
6229
6783
|
}
|
|
6230
|
-
return
|
|
6784
|
+
return base.file.file.endsWith("__init__.py") ? moduleNamed(base.file, name, 1, ctx) : null;
|
|
6231
6785
|
}
|
|
6232
|
-
function
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6236
|
-
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6237
|
-
if (opened.kind !== "module") {
|
|
6238
|
-
continue;
|
|
6239
|
-
}
|
|
6240
|
-
const member = memberOfModule(opened.file, name, ctx, visited);
|
|
6241
|
-
if (member.kind !== "stop") {
|
|
6242
|
-
found.push(member);
|
|
6243
|
-
}
|
|
6786
|
+
function memberKey(base, name, ctx) {
|
|
6787
|
+
if (base.kind === "package") {
|
|
6788
|
+
return null;
|
|
6244
6789
|
}
|
|
6245
|
-
if (
|
|
6246
|
-
return
|
|
6790
|
+
if (base.file.module.moduleScope.bindings.has(name)) {
|
|
6791
|
+
return { kind: "key", key: `${base.file.file}#${name}` };
|
|
6792
|
+
}
|
|
6793
|
+
return openImportMember(base.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6794
|
+
}
|
|
6795
|
+
function moduleNamed(from, module, relativeLevel, ctx) {
|
|
6796
|
+
const resolution = resolveModule(
|
|
6797
|
+
from.file,
|
|
6798
|
+
{ module, relativeLevel },
|
|
6799
|
+
{ roots: ctx.roots }
|
|
6800
|
+
);
|
|
6801
|
+
if (resolution.status !== "resolved") {
|
|
6802
|
+
return resolution.reason === "external" ? { kind: "package", dotted: module, relativeLevel, from } : null;
|
|
6247
6803
|
}
|
|
6248
|
-
|
|
6804
|
+
const file = ctx.filesByPath.get(resolution.file);
|
|
6805
|
+
return file === void 0 ? null : { kind: "module", file };
|
|
6249
6806
|
}
|
|
6250
6807
|
|
|
6251
6808
|
// src/reach/closure.ts
|
|
@@ -6260,7 +6817,9 @@ var REACHABLE_RULES = [
|
|
|
6260
6817
|
function reachedFunctions(seeds, options) {
|
|
6261
6818
|
const ctx = {
|
|
6262
6819
|
filesByPath: new Map(options.files.map((file) => [file.file, file])),
|
|
6263
|
-
roots: options.roots
|
|
6820
|
+
roots: options.roots,
|
|
6821
|
+
facts: options.facts,
|
|
6822
|
+
definitions: options.definitions
|
|
6264
6823
|
};
|
|
6265
6824
|
const db = new Database();
|
|
6266
6825
|
const functionByKey = /* @__PURE__ */ new Map();
|
|
@@ -6282,21 +6841,26 @@ function reachedFunctions(seeds, options) {
|
|
|
6282
6841
|
db.add("entry", [seed.key]);
|
|
6283
6842
|
}
|
|
6284
6843
|
for (; ; ) {
|
|
6285
|
-
|
|
6844
|
+
evaluate(db, REACHABLE_RULES);
|
|
6286
6845
|
const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
|
|
6287
6846
|
if (frontier.length === 0) {
|
|
6288
6847
|
break;
|
|
6289
6848
|
}
|
|
6290
|
-
|
|
6849
|
+
const bodies = frontier.flatMap((key) => {
|
|
6291
6850
|
scanned.add(key);
|
|
6292
6851
|
const source = functionByKey.get(key);
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6852
|
+
return source === void 0 ? [] : [{ key, source, ...bodyOf(source) }];
|
|
6853
|
+
});
|
|
6854
|
+
const spellings = calleeSpellings(
|
|
6855
|
+
bodies.flatMap((body) => body.written),
|
|
6856
|
+
ctx
|
|
6857
|
+
);
|
|
6858
|
+
for (const { key, source, body, written } of bodies) {
|
|
6296
6859
|
const scan = scanBody(
|
|
6297
6860
|
source,
|
|
6298
6861
|
ctx,
|
|
6299
|
-
recognizedCallIds(source.node, options.storageFor(source.file))
|
|
6862
|
+
recognizedCallIds(source.node, options.storageFor(source.file)),
|
|
6863
|
+
{ body, written, spellings }
|
|
6300
6864
|
);
|
|
6301
6865
|
if (scan.stops.length > 0) {
|
|
6302
6866
|
stopsByKey.set(key, scan.stops);
|
|
@@ -6362,7 +6926,18 @@ var EMPTY_SCAN = {
|
|
|
6362
6926
|
parameterCalls: [],
|
|
6363
6927
|
passedPositions: /* @__PURE__ */ new Set()
|
|
6364
6928
|
};
|
|
6365
|
-
function
|
|
6929
|
+
function bodyOf(source) {
|
|
6930
|
+
const { file, node } = source;
|
|
6931
|
+
const body = field(node, "body");
|
|
6932
|
+
if (body === null) {
|
|
6933
|
+
return { body, written: [] };
|
|
6934
|
+
}
|
|
6935
|
+
return {
|
|
6936
|
+
body,
|
|
6937
|
+
written: callsWritten(file, body, scopeAt2(file, node), keyOf(source))
|
|
6938
|
+
};
|
|
6939
|
+
}
|
|
6940
|
+
function scanBody(source, ctx, recognized, read) {
|
|
6366
6941
|
const followed = [];
|
|
6367
6942
|
const stops = [];
|
|
6368
6943
|
const placements = new TargetPlacements();
|
|
@@ -6371,12 +6946,12 @@ function scanBody(source, ctx, recognized) {
|
|
|
6371
6946
|
const seen = /* @__PURE__ */ new Set();
|
|
6372
6947
|
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
6373
6948
|
const { file, node } = source;
|
|
6374
|
-
const
|
|
6375
|
-
if (body === null) {
|
|
6949
|
+
const { written, spellings } = read;
|
|
6950
|
+
if (read.body === null) {
|
|
6376
6951
|
return EMPTY_SCAN;
|
|
6377
6952
|
}
|
|
6378
6953
|
const ownParameters = callParameterNames(node, source.exportPath.length > 1);
|
|
6379
|
-
const recordPassedArgs = (call, callee, calleeKey
|
|
6954
|
+
const recordPassedArgs = (call, callee, calleeKey) => {
|
|
6380
6955
|
const args = field(call, "arguments");
|
|
6381
6956
|
if (args === null) {
|
|
6382
6957
|
return;
|
|
@@ -6385,9 +6960,8 @@ function scanBody(source, ctx, recognized) {
|
|
|
6385
6960
|
if (arg === null || arg.type !== "identifier") {
|
|
6386
6961
|
return;
|
|
6387
6962
|
}
|
|
6388
|
-
const resolved =
|
|
6389
|
-
|
|
6390
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6963
|
+
const resolved = functionNamed(
|
|
6964
|
+
readKey(file.file, arg, enclosingFunction(arg)),
|
|
6391
6965
|
ctx
|
|
6392
6966
|
);
|
|
6393
6967
|
if (resolved === null) {
|
|
@@ -6407,13 +6981,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6407
6981
|
}
|
|
6408
6982
|
});
|
|
6409
6983
|
};
|
|
6410
|
-
const record = (call,
|
|
6984
|
+
const record = (call, site) => {
|
|
6411
6985
|
const callee = calleeText(call);
|
|
6412
|
-
const outcome = resolveCallee2(
|
|
6413
|
-
call,
|
|
6414
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6415
|
-
ctx
|
|
6416
|
-
);
|
|
6986
|
+
const outcome = resolveCallee2(call, site, ctx, spellings);
|
|
6417
6987
|
const placed = outcome.kind === "followed" ? {
|
|
6418
6988
|
file: outcome.target.file.displayPath,
|
|
6419
6989
|
span: spanOf(outcome.target.node)
|
|
@@ -6422,8 +6992,7 @@ function scanBody(source, ctx, recognized) {
|
|
|
6422
6992
|
recordPassedArgs(
|
|
6423
6993
|
call,
|
|
6424
6994
|
callee,
|
|
6425
|
-
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6426
|
-
where
|
|
6995
|
+
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6427
6996
|
);
|
|
6428
6997
|
if (outcome.kind === "stopped") {
|
|
6429
6998
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
@@ -6446,32 +7015,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6446
7015
|
followed.push(outcome.target);
|
|
6447
7016
|
}
|
|
6448
7017
|
};
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
{
|
|
6453
|
-
at: (child, where) => {
|
|
6454
|
-
if (child.type === "call") {
|
|
6455
|
-
record(child, where);
|
|
6456
|
-
}
|
|
6457
|
-
},
|
|
6458
|
-
into: (child, where) => {
|
|
6459
|
-
if (child.type === "function_definition") {
|
|
6460
|
-
return SKIP_CHILDREN2;
|
|
6461
|
-
}
|
|
6462
|
-
if (child.type === "lambda") {
|
|
6463
|
-
return {
|
|
6464
|
-
scope: where.scope,
|
|
6465
|
-
rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
|
|
6466
|
-
};
|
|
6467
|
-
}
|
|
6468
|
-
return {
|
|
6469
|
-
scope: file.module.scopeFor.get(child.id) ?? where.scope,
|
|
6470
|
-
rebound: where.rebound
|
|
6471
|
-
};
|
|
6472
|
-
}
|
|
6473
|
-
}
|
|
6474
|
-
);
|
|
7018
|
+
for (const { call, site } of written) {
|
|
7019
|
+
record(call, site);
|
|
7020
|
+
}
|
|
6475
7021
|
return {
|
|
6476
7022
|
followed,
|
|
6477
7023
|
stops,
|
|
@@ -6481,6 +7027,18 @@ function scanBody(source, ctx, recognized) {
|
|
|
6481
7027
|
passedPositions
|
|
6482
7028
|
};
|
|
6483
7029
|
}
|
|
7030
|
+
function callsWritten(file, body, outer, owner) {
|
|
7031
|
+
const found = [];
|
|
7032
|
+
walkDescendants3(body, outer, {
|
|
7033
|
+
at: (child, scope) => {
|
|
7034
|
+
if (child.type === "call") {
|
|
7035
|
+
found.push({ call: child, site: { file, scope, owner } });
|
|
7036
|
+
}
|
|
7037
|
+
},
|
|
7038
|
+
into: (child, scope) => child.type === "function_definition" ? SKIP_CHILDREN2 : file.module.scopeFor.get(child.id) ?? scope
|
|
7039
|
+
});
|
|
7040
|
+
return found;
|
|
7041
|
+
}
|
|
6484
7042
|
function scopeAt2(file, node) {
|
|
6485
7043
|
let current = node;
|
|
6486
7044
|
while (current !== null) {
|
|
@@ -6492,40 +7050,6 @@ function scopeAt2(file, node) {
|
|
|
6492
7050
|
}
|
|
6493
7051
|
return file.module.moduleScope;
|
|
6494
7052
|
}
|
|
6495
|
-
var REBINDING_FIELDS = {
|
|
6496
|
-
assignment: "left",
|
|
6497
|
-
augmented_assignment: "left",
|
|
6498
|
-
named_expression: "name",
|
|
6499
|
-
for_statement: "left",
|
|
6500
|
-
for_in_clause: "left",
|
|
6501
|
-
as_pattern: "alias"
|
|
6502
|
-
};
|
|
6503
|
-
function reboundNames(body) {
|
|
6504
|
-
const names = /* @__PURE__ */ new Set();
|
|
6505
|
-
walkDescendants3(body, null, {
|
|
6506
|
-
at: (node) => {
|
|
6507
|
-
const target = REBINDING_FIELDS[node.type];
|
|
6508
|
-
if (target === void 0 || boundByBinder(node, body)) {
|
|
6509
|
-
return;
|
|
6510
|
-
}
|
|
6511
|
-
const written = field(node, target);
|
|
6512
|
-
if (written !== null) {
|
|
6513
|
-
for (const name of identifiersUnder(written)) {
|
|
6514
|
-
names.add(name);
|
|
6515
|
-
}
|
|
6516
|
-
}
|
|
6517
|
-
},
|
|
6518
|
-
into: (node) => node.type === "function_definition" ? SKIP_CHILDREN2 : null
|
|
6519
|
-
});
|
|
6520
|
-
return names;
|
|
6521
|
-
}
|
|
6522
|
-
function boundByBinder(node, body) {
|
|
6523
|
-
if (node.type !== "assignment") {
|
|
6524
|
-
return false;
|
|
6525
|
-
}
|
|
6526
|
-
const statement = node.parent;
|
|
6527
|
-
return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
|
|
6528
|
-
}
|
|
6529
7053
|
function identifiersUnder(node, found = []) {
|
|
6530
7054
|
if (node.type === "identifier") {
|
|
6531
7055
|
found.push(node.text);
|
|
@@ -6538,10 +7062,6 @@ function identifiersUnder(node, found = []) {
|
|
|
6538
7062
|
}
|
|
6539
7063
|
return found;
|
|
6540
7064
|
}
|
|
6541
|
-
function lambdaParameters(lambda) {
|
|
6542
|
-
const parameters = field(lambda, "parameters");
|
|
6543
|
-
return parameters === null ? [] : identifiersUnder(parameters);
|
|
6544
|
-
}
|
|
6545
7065
|
function libraryUnit(target, options) {
|
|
6546
7066
|
const { file, node, name, exportPath } = target;
|
|
6547
7067
|
const body = field(node, "body");
|
|
@@ -6692,6 +7212,18 @@ var PythonWrapperIndex = class {
|
|
|
6692
7212
|
ownRoutes = /* @__PURE__ */ new Map();
|
|
6693
7213
|
unitsByKey = /* @__PURE__ */ new Map();
|
|
6694
7214
|
unitsByFile = /* @__PURE__ */ new Map();
|
|
7215
|
+
/** The project function a module-level name in this file refers to, or null for anything else. */
|
|
7216
|
+
functionCalled(file, name) {
|
|
7217
|
+
if (this.options.facts === void 0) {
|
|
7218
|
+
return null;
|
|
7219
|
+
}
|
|
7220
|
+
return functionNamed(nameKeyIn(file, null, name), {
|
|
7221
|
+
filesByPath: this.filesByPath,
|
|
7222
|
+
roots: this.options.roots,
|
|
7223
|
+
facts: this.options.facts,
|
|
7224
|
+
definitions: this.options.definitions
|
|
7225
|
+
});
|
|
7226
|
+
}
|
|
6695
7227
|
register(covers, target, declared) {
|
|
6696
7228
|
const registered = this.registered(target, declared);
|
|
6697
7229
|
const table = covers.kind === "everyRoute" ? this.everyRoute : this.ownRoutes;
|
|
@@ -6738,20 +7270,12 @@ var PythonWrapperIndex = class {
|
|
|
6738
7270
|
if (declared.form.type !== "dependency") {
|
|
6739
7271
|
continue;
|
|
6740
7272
|
}
|
|
6741
|
-
const site = {
|
|
6742
|
-
file,
|
|
6743
|
-
scope: query.module.moduleScope,
|
|
6744
|
-
rebound: /* @__PURE__ */ new Set()
|
|
6745
|
-
};
|
|
6746
7273
|
const targets = [
|
|
6747
7274
|
...decoratorDependencies(query.classification, declared.form),
|
|
6748
7275
|
...parameterDependencies(query.definitionNode, declared.form)
|
|
6749
7276
|
];
|
|
6750
7277
|
for (const name of targets) {
|
|
6751
|
-
const target =
|
|
6752
|
-
filesByPath: this.filesByPath,
|
|
6753
|
-
roots: this.options.roots
|
|
6754
|
-
});
|
|
7278
|
+
const target = this.functionCalled(file.file, name);
|
|
6755
7279
|
if (target !== null) {
|
|
6756
7280
|
found.push(this.registered(target, declared));
|
|
6757
7281
|
}
|
|
@@ -6893,12 +7417,8 @@ function registerConstructorDependencies(file, declared, form, index) {
|
|
|
6893
7417
|
if (listed === void 0) {
|
|
6894
7418
|
continue;
|
|
6895
7419
|
}
|
|
6896
|
-
const site = { file, scope, rebound: /* @__PURE__ */ new Set() };
|
|
6897
7420
|
for (const name of dependencyNamesIn(listed.node, form)) {
|
|
6898
|
-
const target =
|
|
6899
|
-
filesByPath: index.filesByPath,
|
|
6900
|
-
roots: index.options.roots
|
|
6901
|
-
});
|
|
7421
|
+
const target = index.functionCalled(file.file, name);
|
|
6902
7422
|
if (target !== null) {
|
|
6903
7423
|
index.register(
|
|
6904
7424
|
coverageOf(match, file, declared.pack),
|
|
@@ -7226,9 +7746,11 @@ async function extractPythonProject(options) {
|
|
|
7226
7746
|
packs: options.packs,
|
|
7227
7747
|
roots: options.roots,
|
|
7228
7748
|
facts: needsValues ? db : void 0,
|
|
7749
|
+
definitions,
|
|
7229
7750
|
storageFor
|
|
7230
7751
|
})
|
|
7231
7752
|
);
|
|
7753
|
+
const importedDefinition = importedDefinitionLookup(db, bound);
|
|
7232
7754
|
const discovered = /* @__PURE__ */ new Map();
|
|
7233
7755
|
for (const boundFile of bound) {
|
|
7234
7756
|
const { file, root, module: moduleBinding } = boundFile;
|
|
@@ -7242,6 +7764,7 @@ async function extractPythonProject(options) {
|
|
|
7242
7764
|
routerIndex,
|
|
7243
7765
|
gapHandling,
|
|
7244
7766
|
wrappers: wrapperIndex,
|
|
7767
|
+
importedDefinition,
|
|
7245
7768
|
...needsValues ? { facts: db } : {},
|
|
7246
7769
|
...storage === void 0 ? {} : { storage }
|
|
7247
7770
|
})
|
|
@@ -7307,7 +7830,9 @@ async function extractPythonProject(options) {
|
|
|
7307
7830
|
files: bound,
|
|
7308
7831
|
roots: options.roots,
|
|
7309
7832
|
gapHandling,
|
|
7310
|
-
storageFor
|
|
7833
|
+
storageFor,
|
|
7834
|
+
facts: db,
|
|
7835
|
+
definitions
|
|
7311
7836
|
})
|
|
7312
7837
|
);
|
|
7313
7838
|
for (const [key, owners] of summariesBySeed) {
|
|
@@ -7569,7 +8094,7 @@ var PythonWhySession = class {
|
|
|
7569
8094
|
explain(value, options = {}) {
|
|
7570
8095
|
return explainResolvedKey({
|
|
7571
8096
|
db: this.db,
|
|
7572
|
-
rules:
|
|
8097
|
+
rules: RESOLUTION_RULES,
|
|
7573
8098
|
key: readKey(value.file, value.node, enclosingFunction(value.node)),
|
|
7574
8099
|
locate: (key) => this.locate(key),
|
|
7575
8100
|
displayPath: (key) => this.displayPath(key),
|
|
@@ -7699,6 +8224,7 @@ export {
|
|
|
7699
8224
|
emitValueFacts,
|
|
7700
8225
|
extractPythonProject,
|
|
7701
8226
|
findPythonFiles,
|
|
8227
|
+
nameKeyIn,
|
|
7702
8228
|
nodeId,
|
|
7703
8229
|
objectReturnedBy,
|
|
7704
8230
|
parsePython,
|