@suss/adapter-python 0.27.0 → 0.29.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 +725 -453
- package/dist/index.d.cts +67 -12
- package/dist/index.d.ts +67 -12
- package/dist/index.js +718 -441
- 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) {
|
|
@@ -1408,14 +1413,14 @@ function invocationEffects(definitionNode) {
|
|
|
1408
1413
|
if (calls.length === 0) {
|
|
1409
1414
|
return [];
|
|
1410
1415
|
}
|
|
1411
|
-
const
|
|
1416
|
+
const statementOf3 = /* @__PURE__ */ new Map();
|
|
1412
1417
|
const statements = [];
|
|
1413
1418
|
for (const call of calls) {
|
|
1414
1419
|
const statement = enclosingStatement(call, body);
|
|
1415
1420
|
if (statement === null) {
|
|
1416
1421
|
continue;
|
|
1417
1422
|
}
|
|
1418
|
-
|
|
1423
|
+
statementOf3.set(call.id, statement);
|
|
1419
1424
|
statements.push(statement);
|
|
1420
1425
|
}
|
|
1421
1426
|
const lowered = lowerPythonBody(body, statements);
|
|
@@ -1427,7 +1432,7 @@ function invocationEffects(definitionNode) {
|
|
|
1427
1432
|
statements
|
|
1428
1433
|
);
|
|
1429
1434
|
return calls.map((call) => {
|
|
1430
|
-
const statement =
|
|
1435
|
+
const statement = statementOf3.get(call.id);
|
|
1431
1436
|
const conditions = sharedGatingConditions(
|
|
1432
1437
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement),
|
|
1433
1438
|
predicateOf
|
|
@@ -1446,40 +1451,22 @@ function invocationEffects(definitionNode) {
|
|
|
1446
1451
|
import { mountPathsOf } from "@suss/resolution";
|
|
1447
1452
|
|
|
1448
1453
|
// src/facts/resolve.ts
|
|
1449
|
-
import { deriveOnDemand, evaluate } from "@suss/datalog";
|
|
1450
1454
|
import {
|
|
1451
|
-
|
|
1455
|
+
askResolution,
|
|
1452
1456
|
placeholderValues,
|
|
1453
|
-
RESOLUTION_QUESTIONS,
|
|
1454
|
-
RESOLUTION_RULES,
|
|
1455
1457
|
writtenValueOf as sharedWrittenValueOf,
|
|
1456
1458
|
singleAnswers
|
|
1457
1459
|
} from "@suss/resolution";
|
|
1458
|
-
var RESOLUTION_PROGRAM = deriveOnDemand(
|
|
1459
|
-
[...RESOLUTION_RULES, ...RESOLUTION_QUESTIONS],
|
|
1460
|
-
ANSWER_RELATIONS
|
|
1461
|
-
);
|
|
1462
1460
|
function resolveCalls(db, callKeys) {
|
|
1463
|
-
|
|
1464
|
-
const fresh = callKeys.filter((key) => !asked.has(key));
|
|
1465
|
-
if (fresh.length === 0) {
|
|
1466
|
-
return;
|
|
1467
|
-
}
|
|
1468
|
-
for (const callKey of fresh) {
|
|
1469
|
-
db.add("wanted", [callKey]);
|
|
1470
|
-
}
|
|
1471
|
-
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1461
|
+
askResolution(db, callKeys);
|
|
1472
1462
|
}
|
|
1473
1463
|
function writtenValueOf(db, key) {
|
|
1474
1464
|
resolveCalls(db, [key]);
|
|
1475
1465
|
return sharedWrittenValueOf(db, key, (keys) => resolveCalls(db, keys));
|
|
1476
1466
|
}
|
|
1477
|
-
function originsOf(db,
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1481
|
-
}
|
|
1482
|
-
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
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]) }));
|
|
1483
1470
|
}
|
|
1484
1471
|
function subjectConstructions(db, valueKeys) {
|
|
1485
1472
|
const found = /* @__PURE__ */ new Map();
|
|
@@ -1487,10 +1474,7 @@ function subjectConstructions(db, valueKeys) {
|
|
|
1487
1474
|
return found;
|
|
1488
1475
|
}
|
|
1489
1476
|
const askSubjects = (keys) => {
|
|
1490
|
-
|
|
1491
|
-
db.add("wantedSubject", [key]);
|
|
1492
|
-
}
|
|
1493
|
-
evaluate(db, RESOLUTION_PROGRAM.rules);
|
|
1477
|
+
askResolution(db, keys, "wantedSubject");
|
|
1494
1478
|
};
|
|
1495
1479
|
askSubjects(valueKeys);
|
|
1496
1480
|
const placeholders = placeholderValues(db);
|
|
@@ -1537,6 +1521,11 @@ function objectReturnedBy(db, callKey) {
|
|
|
1537
1521
|
}
|
|
1538
1522
|
|
|
1539
1523
|
// src/facts/values.ts
|
|
1524
|
+
import {
|
|
1525
|
+
startsAtName,
|
|
1526
|
+
valueLeftByWrites,
|
|
1527
|
+
writesRunInOrder
|
|
1528
|
+
} from "@suss/resolution";
|
|
1540
1529
|
function nodeId(filePath, node) {
|
|
1541
1530
|
return `${filePath}:${node.startIndex}-${node.endIndex}`;
|
|
1542
1531
|
}
|
|
@@ -1547,10 +1536,10 @@ function readKey(filePath, node, enclosing) {
|
|
|
1547
1536
|
if (node.type !== "identifier") {
|
|
1548
1537
|
return nodeId(filePath, node);
|
|
1549
1538
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
return
|
|
1539
|
+
return nameKeyIn(filePath, enclosing, node.text);
|
|
1540
|
+
}
|
|
1541
|
+
function nameKeyIn(filePath, enclosing, name) {
|
|
1542
|
+
return nameKey(filePath, scopeChainOf(filePath, enclosing), name);
|
|
1554
1543
|
}
|
|
1555
1544
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
1556
1545
|
"string",
|
|
@@ -1588,9 +1577,6 @@ function parameterName2(param) {
|
|
|
1588
1577
|
}
|
|
1589
1578
|
return children(param).find((child) => child.type === "identifier") ?? null;
|
|
1590
1579
|
}
|
|
1591
|
-
function parameterList(fn) {
|
|
1592
|
-
return parameterShapes(fn).map((parameter) => parameter.name);
|
|
1593
|
-
}
|
|
1594
1580
|
function parameterShapes(fn) {
|
|
1595
1581
|
const params = field(fn, "parameters");
|
|
1596
1582
|
const declared = [];
|
|
@@ -1605,24 +1591,52 @@ function parameterShapes(fn) {
|
|
|
1605
1591
|
}
|
|
1606
1592
|
return declared;
|
|
1607
1593
|
}
|
|
1608
|
-
function
|
|
1609
|
-
|
|
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;
|
|
1610
1604
|
}
|
|
1611
1605
|
function add(emitter, relation, ...tuple) {
|
|
1612
1606
|
emitter.db.add(relation, tuple);
|
|
1613
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
|
+
}
|
|
1614
1631
|
function emitCall(emitter, call) {
|
|
1615
1632
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
1616
1633
|
const callee = field(call, "function");
|
|
1617
|
-
|
|
1634
|
+
const args = field(call, "arguments");
|
|
1635
|
+
if (callee === null || args === null) {
|
|
1618
1636
|
return;
|
|
1619
1637
|
}
|
|
1620
1638
|
const callKey = nodeId(emitter.filePath, call);
|
|
1621
1639
|
add(emitter, "call", callKey, valueKey(emitter, callee));
|
|
1622
|
-
const args = field(call, "arguments");
|
|
1623
|
-
if (args === null) {
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
1640
|
let position = 0;
|
|
1627
1641
|
for (const argument of children(args)) {
|
|
1628
1642
|
if (argument.type === "keyword_argument") {
|
|
@@ -1653,11 +1667,7 @@ function valueKey(emitter, value) {
|
|
|
1653
1667
|
if (value.type !== "identifier") {
|
|
1654
1668
|
return nodeId(emitter.filePath, value);
|
|
1655
1669
|
}
|
|
1656
|
-
|
|
1657
|
-
if (enclosing !== null && enclosing.params.has(value.text)) {
|
|
1658
|
-
return `${enclosing.funcKey}#${value.text}`;
|
|
1659
|
-
}
|
|
1660
|
-
return nameId(emitter.filePath, value.text);
|
|
1670
|
+
return nameKey(emitter.filePath, emitter.enclosing, value.text);
|
|
1661
1671
|
}
|
|
1662
1672
|
function emitSequence(emitter, sequence) {
|
|
1663
1673
|
const objectKey = nodeId(emitter.filePath, sequence);
|
|
@@ -1747,7 +1757,6 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1747
1757
|
const funcKey = nodeId(emitter.filePath, fn);
|
|
1748
1758
|
add(emitter, "func", funcKey);
|
|
1749
1759
|
const params = field(fn, "parameters");
|
|
1750
|
-
const declared = parameterNames(fn);
|
|
1751
1760
|
let position = classKey === void 0 ? 0 : -1;
|
|
1752
1761
|
let byPosition = true;
|
|
1753
1762
|
let receiver = null;
|
|
@@ -1770,24 +1779,22 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1770
1779
|
}
|
|
1771
1780
|
position += 1;
|
|
1772
1781
|
}
|
|
1782
|
+
const reading = scopeReadingOf(fn);
|
|
1773
1783
|
const inside = {
|
|
1774
1784
|
...emitter,
|
|
1775
|
-
enclosing: {
|
|
1785
|
+
enclosing: {
|
|
1786
|
+
funcKey,
|
|
1787
|
+
locals: reading.locals,
|
|
1788
|
+
globals: reading.globals,
|
|
1789
|
+
parent: emitter.enclosing
|
|
1790
|
+
}
|
|
1776
1791
|
};
|
|
1792
|
+
emitScopeWrites(inside, reading, false);
|
|
1777
1793
|
const body = field(fn, "body");
|
|
1778
1794
|
if (body === null) {
|
|
1779
1795
|
return funcKey;
|
|
1780
1796
|
}
|
|
1781
|
-
|
|
1782
|
-
for (const child of children(node)) {
|
|
1783
|
-
if (isFunction(child)) {
|
|
1784
|
-
add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
|
|
1785
|
-
continue;
|
|
1786
|
-
}
|
|
1787
|
-
recordNested(child);
|
|
1788
|
-
}
|
|
1789
|
-
};
|
|
1790
|
-
recordNested(body);
|
|
1797
|
+
emitNestedDefinitions(inside, body);
|
|
1791
1798
|
walkExpressions(inside, body, (child) => {
|
|
1792
1799
|
if (child.type === "return_statement") {
|
|
1793
1800
|
const returned = child.namedChildren[0];
|
|
@@ -1798,29 +1805,17 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1798
1805
|
if (child.type === "call") {
|
|
1799
1806
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1800
1807
|
}
|
|
1801
|
-
if (child.type === "assignment") {
|
|
1802
|
-
|
|
1808
|
+
if (child.type === "assignment" && receiver !== null) {
|
|
1809
|
+
emitReceiverProperty(inside, child, receiver);
|
|
1803
1810
|
}
|
|
1804
1811
|
emitExpressionFact(inside, child);
|
|
1805
1812
|
});
|
|
1806
1813
|
return funcKey;
|
|
1807
1814
|
}
|
|
1808
|
-
function
|
|
1815
|
+
function emitReceiverProperty(emitter, assignment2, receiver) {
|
|
1809
1816
|
const left = field(assignment2, "left");
|
|
1810
1817
|
const right = field(assignment2, "right");
|
|
1811
|
-
if (left === null || right === null) {
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
if (left.type === "identifier") {
|
|
1815
|
-
add(
|
|
1816
|
-
emitter,
|
|
1817
|
-
"binds",
|
|
1818
|
-
nameId(emitter.filePath, left.text),
|
|
1819
|
-
valueKey(emitter, assignedValue(right))
|
|
1820
|
-
);
|
|
1821
|
-
return;
|
|
1822
|
-
}
|
|
1823
|
-
if (receiver === null || left.type !== "attribute") {
|
|
1818
|
+
if (left === null || right === null || left.type !== "attribute") {
|
|
1824
1819
|
return;
|
|
1825
1820
|
}
|
|
1826
1821
|
const object = field(left, "object");
|
|
@@ -1836,21 +1831,6 @@ function emitBinding(emitter, assignment2, receiver) {
|
|
|
1836
1831
|
valueKey(emitter, right)
|
|
1837
1832
|
);
|
|
1838
1833
|
}
|
|
1839
|
-
function emitAssignment(emitter, assignment2) {
|
|
1840
|
-
emitBinding(emitter, assignment2, null);
|
|
1841
|
-
const left = field(assignment2, "left");
|
|
1842
|
-
const right = field(assignment2, "right");
|
|
1843
|
-
if (left === null || right === null || left.type !== "identifier") {
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
add(
|
|
1847
|
-
emitter,
|
|
1848
|
-
"exportsAs",
|
|
1849
|
-
emitter.filePath,
|
|
1850
|
-
left.text,
|
|
1851
|
-
valueKey(emitter, assignedValue(right))
|
|
1852
|
-
);
|
|
1853
|
-
}
|
|
1854
1834
|
function assignedValue(right) {
|
|
1855
1835
|
if (right.type !== "subscript") {
|
|
1856
1836
|
return right;
|
|
@@ -1877,12 +1857,19 @@ function declaredMemberKey(emitter, member, classKey) {
|
|
|
1877
1857
|
}
|
|
1878
1858
|
return null;
|
|
1879
1859
|
}
|
|
1860
|
+
function writtenBaseName(base) {
|
|
1861
|
+
return base.type === "identifier" || base.type === "attribute" ? base.text : null;
|
|
1862
|
+
}
|
|
1880
1863
|
function emitClassFacts(emitter, cls) {
|
|
1881
1864
|
const classKey = nodeId(emitter.filePath, cls);
|
|
1882
1865
|
add(emitter, "objectValue", classKey);
|
|
1883
1866
|
const bases = field(cls, "superclasses");
|
|
1884
1867
|
for (const base of bases === null ? [] : children(bases)) {
|
|
1885
1868
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
1869
|
+
const written = writtenBaseName(base);
|
|
1870
|
+
if (written !== null) {
|
|
1871
|
+
add(emitter, "extendsNamed", classKey, written);
|
|
1872
|
+
}
|
|
1886
1873
|
}
|
|
1887
1874
|
const body = field(cls, "body");
|
|
1888
1875
|
for (const statement of body === null ? [] : children(body)) {
|
|
@@ -1901,40 +1888,361 @@ function emitClassFacts(emitter, cls) {
|
|
|
1901
1888
|
}
|
|
1902
1889
|
continue;
|
|
1903
1890
|
}
|
|
1904
|
-
const
|
|
1891
|
+
const memberKey2 = declaredMemberKey(emitter, member, classKey);
|
|
1905
1892
|
const name = field(member, "name");
|
|
1906
|
-
if (
|
|
1907
|
-
add(emitter, "holdsProperty", classKey, name.text,
|
|
1893
|
+
if (memberKey2 !== null && name !== null) {
|
|
1894
|
+
add(emitter, "holdsProperty", classKey, name.text, memberKey2);
|
|
1908
1895
|
}
|
|
1909
1896
|
}
|
|
1910
1897
|
return classKey;
|
|
1911
1898
|
}
|
|
1912
|
-
function
|
|
1913
|
-
const
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1899
|
+
function emitNestedDefinitions(emitter, node) {
|
|
1900
|
+
for (const child of children(node)) {
|
|
1901
|
+
if (isFunction(child)) {
|
|
1902
|
+
emitFunctionFacts(emitter, child);
|
|
1903
|
+
if (emitter.enclosing !== null) {
|
|
1904
|
+
add(
|
|
1905
|
+
emitter,
|
|
1906
|
+
"containsFn",
|
|
1907
|
+
emitter.enclosing.funcKey,
|
|
1908
|
+
nodeId(emitter.filePath, child)
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
if (child.type === "class_definition") {
|
|
1914
|
+
emitClassFacts(emitter, child);
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
emitNestedDefinitions(emitter, child);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
var readingsByTree = /* @__PURE__ */ new WeakMap();
|
|
1921
|
+
function scopeReadingOf(fn) {
|
|
1922
|
+
const tree = fn.tree;
|
|
1923
|
+
let byNode = readingsByTree.get(tree);
|
|
1924
|
+
if (byNode === void 0) {
|
|
1925
|
+
byNode = /* @__PURE__ */ new Map();
|
|
1926
|
+
readingsByTree.set(tree, byNode);
|
|
1927
|
+
}
|
|
1928
|
+
const remembered = byNode.get(fn.id);
|
|
1929
|
+
if (remembered !== void 0) {
|
|
1930
|
+
return remembered;
|
|
1931
|
+
}
|
|
1932
|
+
const body = field(fn, "body");
|
|
1933
|
+
const reading = readScope(
|
|
1934
|
+
body !== null && body.type === "block" ? body : null,
|
|
1935
|
+
boundParameterNames(fn)
|
|
1936
|
+
);
|
|
1937
|
+
byNode.set(fn.id, reading);
|
|
1938
|
+
return reading;
|
|
1939
|
+
}
|
|
1940
|
+
var TARGET_PATTERN_TYPES = /* @__PURE__ */ new Set([
|
|
1941
|
+
"pattern_list",
|
|
1942
|
+
"tuple_pattern",
|
|
1943
|
+
"list_pattern"
|
|
1944
|
+
]);
|
|
1945
|
+
function targetNames(target, found = []) {
|
|
1946
|
+
if (target.type === "identifier") {
|
|
1947
|
+
found.push(target);
|
|
1948
|
+
return found;
|
|
1949
|
+
}
|
|
1950
|
+
if (TARGET_PATTERN_TYPES.has(target.type)) {
|
|
1951
|
+
for (const child of children(target)) {
|
|
1952
|
+
targetNames(child, found);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
return found;
|
|
1956
|
+
}
|
|
1957
|
+
function readAssignment(node, sink) {
|
|
1958
|
+
const left = field(node, "left");
|
|
1959
|
+
const right = field(node, "right");
|
|
1960
|
+
if (left === null) {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
const written = right === null ? null : assignedValue(right);
|
|
1964
|
+
const names = targetNames(left);
|
|
1965
|
+
for (const name of names) {
|
|
1966
|
+
sink.record(name.text, {
|
|
1967
|
+
// An unpacked target takes one piece of the value, and which piece
|
|
1968
|
+
// is not something the assignment says.
|
|
1969
|
+
value: names.length === 1 ? written : null,
|
|
1970
|
+
given: null,
|
|
1971
|
+
at: node,
|
|
1972
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
function readAugmentedAssignment(node, sink) {
|
|
1977
|
+
const left = field(node, "left");
|
|
1978
|
+
if (left === null || left.type !== "identifier") {
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
sink.record(left.text, {
|
|
1982
|
+
value: null,
|
|
1983
|
+
given: null,
|
|
1984
|
+
at: node,
|
|
1985
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
function readIterationTarget(node, sink) {
|
|
1989
|
+
const left = field(node, "left");
|
|
1990
|
+
if (left === null) {
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
for (const name of targetNames(left)) {
|
|
1994
|
+
sink.record(name.text, {
|
|
1995
|
+
value: null,
|
|
1996
|
+
given: null,
|
|
1997
|
+
at: node,
|
|
1998
|
+
direct: false
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
function identifierOf(node) {
|
|
2003
|
+
if (node.type === "identifier") {
|
|
2004
|
+
return node;
|
|
2005
|
+
}
|
|
2006
|
+
return children(node).find((child) => child.type === "identifier") ?? null;
|
|
2007
|
+
}
|
|
2008
|
+
function readAsPattern(node, sink) {
|
|
2009
|
+
const target = field(node, "alias") ?? children(node)[1];
|
|
2010
|
+
const name = target === void 0 ? null : identifierOf(target);
|
|
2011
|
+
if (name === null) {
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
sink.record(name.text, {
|
|
2015
|
+
value: null,
|
|
2016
|
+
given: null,
|
|
2017
|
+
at: node,
|
|
2018
|
+
direct: false
|
|
2019
|
+
});
|
|
2020
|
+
}
|
|
2021
|
+
function readWalrus(node, sink) {
|
|
2022
|
+
const name = field(node, "name");
|
|
2023
|
+
if (name === null || name.type !== "identifier") {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
sink.record(name.text, {
|
|
2027
|
+
value: field(node, "value"),
|
|
2028
|
+
given: null,
|
|
2029
|
+
at: node,
|
|
2030
|
+
direct: false
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
function readGlobalStatement(node, sink) {
|
|
2034
|
+
for (const child of children(node)) {
|
|
2035
|
+
if (child.type === "identifier") {
|
|
2036
|
+
sink.globals.add(child.text);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
function readNonlocalStatement(node, sink) {
|
|
2041
|
+
for (const child of children(node)) {
|
|
2042
|
+
if (child.type === "identifier") {
|
|
2043
|
+
sink.nonlocals.add(child.text);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
var WRITE_READERS = {
|
|
2048
|
+
assignment: readAssignment,
|
|
2049
|
+
augmented_assignment: readAugmentedAssignment,
|
|
2050
|
+
for_statement: readIterationTarget,
|
|
2051
|
+
for_in_clause: readIterationTarget,
|
|
2052
|
+
as_pattern: readAsPattern,
|
|
2053
|
+
named_expression: readWalrus,
|
|
2054
|
+
global_statement: readGlobalStatement,
|
|
2055
|
+
nonlocal_statement: readNonlocalStatement
|
|
2056
|
+
};
|
|
2057
|
+
function readScope(bodyOwner, parameterNames) {
|
|
2058
|
+
const writes = /* @__PURE__ */ new Map();
|
|
2059
|
+
const globals = /* @__PURE__ */ new Set();
|
|
2060
|
+
const nonlocals = /* @__PURE__ */ new Set();
|
|
2061
|
+
const record = (name, write) => {
|
|
2062
|
+
const existing = writes.get(name);
|
|
2063
|
+
if (existing === void 0) {
|
|
2064
|
+
writes.set(name, [write]);
|
|
1917
2065
|
return;
|
|
1918
2066
|
}
|
|
1919
|
-
|
|
1920
|
-
add(emitter, "exportsAs", filePath, name.text, key);
|
|
2067
|
+
existing.push(write);
|
|
1921
2068
|
};
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2069
|
+
const sink = { bodyOwner, globals, nonlocals, record };
|
|
2070
|
+
const visit = (node) => {
|
|
2071
|
+
if (isFunction(node) || node.type === "class_definition") {
|
|
2072
|
+
const name = field(node, "name");
|
|
2073
|
+
if (name !== null) {
|
|
2074
|
+
record(name.text, {
|
|
2075
|
+
value: node,
|
|
2076
|
+
given: null,
|
|
2077
|
+
at: node,
|
|
2078
|
+
direct: isDirectStatement(node, bodyOwner)
|
|
2079
|
+
});
|
|
1933
2080
|
}
|
|
1934
|
-
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
WRITE_READERS[node.type]?.(node, sink);
|
|
2084
|
+
for (const child of children(node)) {
|
|
2085
|
+
visit(child);
|
|
1935
2086
|
}
|
|
1936
2087
|
};
|
|
1937
|
-
|
|
2088
|
+
for (const statement of bodyOwner === null ? [] : children(bodyOwner)) {
|
|
2089
|
+
visit(statement);
|
|
2090
|
+
}
|
|
2091
|
+
const locals = new Set(writes.keys());
|
|
2092
|
+
for (const name of [...globals, ...nonlocals]) {
|
|
2093
|
+
locals.delete(name);
|
|
2094
|
+
}
|
|
2095
|
+
for (const name of parameterNames) {
|
|
2096
|
+
locals.add(name);
|
|
2097
|
+
addParameterWrite(writes.get(name), name);
|
|
2098
|
+
}
|
|
2099
|
+
return { bodyOwner, writes, locals, globals };
|
|
2100
|
+
}
|
|
2101
|
+
function addParameterWrite(later, name) {
|
|
2102
|
+
const first = later?.[0];
|
|
2103
|
+
if (later === void 0 || first === void 0) {
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
later.unshift({ value: null, given: name, at: first.at, direct: false });
|
|
2107
|
+
}
|
|
2108
|
+
function statementOf(node) {
|
|
2109
|
+
let current = node;
|
|
2110
|
+
while (current !== null) {
|
|
2111
|
+
const parent = current.parent;
|
|
2112
|
+
if (parent === null) {
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
if (parent.type === "block" || parent.type === "module") {
|
|
2116
|
+
return current;
|
|
2117
|
+
}
|
|
2118
|
+
current = parent;
|
|
2119
|
+
}
|
|
2120
|
+
return null;
|
|
2121
|
+
}
|
|
2122
|
+
function isDirectStatement(node, bodyOwner) {
|
|
2123
|
+
if (bodyOwner === null) {
|
|
2124
|
+
return false;
|
|
2125
|
+
}
|
|
2126
|
+
const statement = statementOf(node);
|
|
2127
|
+
return statement !== null && statement.parent?.id === bodyOwner.id;
|
|
2128
|
+
}
|
|
2129
|
+
var UNREAD_PARENTS = /* @__PURE__ */ new Set([
|
|
2130
|
+
"as_pattern_target",
|
|
2131
|
+
"global_statement",
|
|
2132
|
+
"nonlocal_statement",
|
|
2133
|
+
"pattern_list",
|
|
2134
|
+
"tuple_pattern",
|
|
2135
|
+
"list_pattern",
|
|
2136
|
+
"dotted_name"
|
|
2137
|
+
]);
|
|
2138
|
+
var UNREAD_FIELDS = {
|
|
2139
|
+
assignment: "left",
|
|
2140
|
+
augmented_assignment: "left",
|
|
2141
|
+
for_statement: "left",
|
|
2142
|
+
for_in_clause: "left",
|
|
2143
|
+
named_expression: "name",
|
|
2144
|
+
function_definition: "name",
|
|
2145
|
+
class_definition: "name",
|
|
2146
|
+
attribute: "attribute",
|
|
2147
|
+
keyword_argument: "name"
|
|
2148
|
+
};
|
|
2149
|
+
function isNameRead(name) {
|
|
2150
|
+
const parent = name.parent;
|
|
2151
|
+
if (parent === null) {
|
|
2152
|
+
return true;
|
|
2153
|
+
}
|
|
2154
|
+
if (UNREAD_PARENTS.has(parent.type)) {
|
|
2155
|
+
return false;
|
|
2156
|
+
}
|
|
2157
|
+
const fieldName = UNREAD_FIELDS[parent.type];
|
|
2158
|
+
return fieldName === void 0 || field(parent, fieldName)?.id !== name.id;
|
|
2159
|
+
}
|
|
2160
|
+
var READ_FIRST = {
|
|
2161
|
+
call: "function",
|
|
2162
|
+
attribute: "object"
|
|
2163
|
+
};
|
|
2164
|
+
function readFirst(node) {
|
|
2165
|
+
if (node.type === "parenthesized_expression") {
|
|
2166
|
+
return children(node)[0] ?? null;
|
|
2167
|
+
}
|
|
2168
|
+
const fieldName = READ_FIRST[node.type];
|
|
2169
|
+
return fieldName === void 0 ? null : field(node, fieldName);
|
|
2170
|
+
}
|
|
2171
|
+
var NAME_READS = {
|
|
2172
|
+
nameTypes: /* @__PURE__ */ new Set(["identifier"]),
|
|
2173
|
+
laterBodies: LATER_BODY_TYPES,
|
|
2174
|
+
childrenOf: children,
|
|
2175
|
+
isRead: isNameRead,
|
|
2176
|
+
readFirst
|
|
2177
|
+
};
|
|
2178
|
+
var CONSTRUCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2179
|
+
"call",
|
|
2180
|
+
"list",
|
|
2181
|
+
"dictionary",
|
|
2182
|
+
"set",
|
|
2183
|
+
"tuple",
|
|
2184
|
+
"list_comprehension",
|
|
2185
|
+
"dictionary_comprehension",
|
|
2186
|
+
"set_comprehension",
|
|
2187
|
+
"generator_expression"
|
|
2188
|
+
]);
|
|
2189
|
+
function describeWrite(emitter, write, name) {
|
|
2190
|
+
if (write.value === null) {
|
|
2191
|
+
return {
|
|
2192
|
+
value: write.given,
|
|
2193
|
+
placeholder: false,
|
|
2194
|
+
construction: null,
|
|
2195
|
+
narrowsName: false
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
return {
|
|
2199
|
+
value: valueKey(emitter, write.value),
|
|
2200
|
+
placeholder: write.value.type === "none",
|
|
2201
|
+
construction: CONSTRUCTION_TYPES.has(write.value.type) ? write.value.text.replace(/\s+/g, " ").trim() : null,
|
|
2202
|
+
narrowsName: write.value.type === "call" && startsAtName(write.value, name, NAME_READS)
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2205
|
+
function settledValue(emitter, reading, name, writes) {
|
|
2206
|
+
const only = writes.length === 1 ? writes[0] : void 0;
|
|
2207
|
+
if (only !== void 0) {
|
|
2208
|
+
return only.value === null ? only.given : valueKey(emitter, only.value);
|
|
2209
|
+
}
|
|
2210
|
+
return valueLeftByWrites(
|
|
2211
|
+
writes.map((write) => describeWrite(emitter, write, name)),
|
|
2212
|
+
writesRunInOrder(reading.bodyOwner, name, writes, NAME_READS)
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
function emitScopeWrites(emitter, reading, atModule) {
|
|
2216
|
+
for (const [name, writes] of reading.writes) {
|
|
2217
|
+
const settled = settledValue(emitter, reading, name, writes);
|
|
2218
|
+
const key = nameKey(emitter.filePath, emitter.enclosing, name);
|
|
2219
|
+
if (settled === null) {
|
|
2220
|
+
emitCandidates(emitter, key, name, writes);
|
|
2221
|
+
continue;
|
|
2222
|
+
}
|
|
2223
|
+
add(emitter, writes.length === 1 ? "binds" : "endsHolding", key, settled);
|
|
2224
|
+
if (atModule) {
|
|
2225
|
+
add(emitter, "exportsAs", emitter.filePath, name, settled);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
function emitCandidates(emitter, key, name, writes) {
|
|
2230
|
+
for (const write of writes) {
|
|
2231
|
+
if (write.value === null) {
|
|
2232
|
+
if (write.given === null) {
|
|
2233
|
+
add(emitter, "writesUnstated", key);
|
|
2234
|
+
}
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
if (!describeWrite(emitter, write, name).narrowsName) {
|
|
2238
|
+
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
function emitValueFacts(db, filePath, root) {
|
|
2243
|
+
const emitter = { db, filePath, enclosing: null };
|
|
2244
|
+
emitNestedDefinitions(emitter, root);
|
|
2245
|
+
emitScopeWrites(emitter, readScope(root, []), true);
|
|
1938
2246
|
emitExpressionFacts(emitter, root);
|
|
1939
2247
|
}
|
|
1940
2248
|
|
|
@@ -2047,7 +2355,7 @@ function pythonLowering(options) {
|
|
|
2047
2355
|
return {
|
|
2048
2356
|
idOf: (node) => node.id,
|
|
2049
2357
|
expression: (node) => expressionOf(node, originOf2),
|
|
2050
|
-
statement:
|
|
2358
|
+
statement: statementOf2,
|
|
2051
2359
|
siteOf,
|
|
2052
2360
|
functionOf,
|
|
2053
2361
|
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
@@ -2311,7 +2619,7 @@ var STATEMENT_TYPES = {
|
|
|
2311
2619
|
body: blockStatements2(field(node, "body"))
|
|
2312
2620
|
})
|
|
2313
2621
|
};
|
|
2314
|
-
function
|
|
2622
|
+
function statementOf2(node) {
|
|
2315
2623
|
const lower = STATEMENT_TYPES[node.type];
|
|
2316
2624
|
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2317
2625
|
}
|
|
@@ -3381,9 +3689,13 @@ function constructionThroughFacts(name, scope, scan) {
|
|
|
3381
3689
|
if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
|
|
3382
3690
|
return null;
|
|
3383
3691
|
}
|
|
3384
|
-
const
|
|
3385
|
-
|
|
3386
|
-
|
|
3692
|
+
const nameKey2 = nameKeyIn(
|
|
3693
|
+
scan.bound.file,
|
|
3694
|
+
scope.kind === "function" ? scope.node : enclosingFunction(scope.node),
|
|
3695
|
+
name
|
|
3696
|
+
);
|
|
3697
|
+
const constructionKey2 = subjectConstructions(facts, [nameKey2]).get(
|
|
3698
|
+
nameKey2
|
|
3387
3699
|
)?.constructionKey;
|
|
3388
3700
|
return constructionKey2 === void 0 ? null : scan.index.byValueKey.get(constructionKey2) ?? null;
|
|
3389
3701
|
}
|
|
@@ -3474,11 +3786,11 @@ function walkStatements(statements, position, scan, visit) {
|
|
|
3474
3786
|
visit(stmt, position, scan);
|
|
3475
3787
|
}
|
|
3476
3788
|
}
|
|
3477
|
-
function
|
|
3789
|
+
function targetNames2(node) {
|
|
3478
3790
|
if (node.type === "identifier") {
|
|
3479
3791
|
return [node.text];
|
|
3480
3792
|
}
|
|
3481
|
-
return bodyStatements(node).flatMap(
|
|
3793
|
+
return bodyStatements(node).flatMap(targetNames2);
|
|
3482
3794
|
}
|
|
3483
3795
|
function sequenceElementNames(node) {
|
|
3484
3796
|
if (node === null || node.type !== "list" && node.type !== "tuple") {
|
|
@@ -3515,7 +3827,7 @@ function loopBindingsOf(stmt, position, scan) {
|
|
|
3515
3827
|
});
|
|
3516
3828
|
return bindings;
|
|
3517
3829
|
}
|
|
3518
|
-
for (const name of
|
|
3830
|
+
for (const name of targetNames2(left)) {
|
|
3519
3831
|
bindings.set(name, { kind: "unenumerable", location });
|
|
3520
3832
|
}
|
|
3521
3833
|
return bindings;
|
|
@@ -4991,14 +5303,9 @@ function matchedChains(calls, options) {
|
|
|
4991
5303
|
if (chains.length === 0) {
|
|
4992
5304
|
return [];
|
|
4993
5305
|
}
|
|
4994
|
-
const alreadyAsked = new Set(
|
|
4995
|
-
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
4996
|
-
);
|
|
4997
5306
|
resolveCalls(
|
|
4998
5307
|
options.facts,
|
|
4999
|
-
storageCallees(calls, options.filePath, options.couldMatch)
|
|
5000
|
-
(key) => !alreadyAsked.has(key)
|
|
5001
|
-
)
|
|
5308
|
+
storageCallees(calls, options.filePath, options.couldMatch)
|
|
5002
5309
|
);
|
|
5003
5310
|
const matched = [];
|
|
5004
5311
|
for (const chain of chains) {
|
|
@@ -6060,7 +6367,7 @@ function isInjectedParameter(param, injectedCallees, scope, ctx) {
|
|
|
6060
6367
|
import fs4 from "fs";
|
|
6061
6368
|
import path4 from "path";
|
|
6062
6369
|
import { Database as Database3 } from "@suss/datalog";
|
|
6063
|
-
import { explainResolvedKey, RESOLUTION_RULES
|
|
6370
|
+
import { explainResolvedKey, RESOLUTION_RULES } from "@suss/resolution";
|
|
6064
6371
|
|
|
6065
6372
|
// src/facts.ts
|
|
6066
6373
|
function unitKey(filePath, range, name) {
|
|
@@ -6078,7 +6385,7 @@ function emitModuleImportFacts(db, filePath, module, resolverOptions) {
|
|
|
6078
6385
|
if (binding.kind !== "import" && binding.kind !== "importFrom") {
|
|
6079
6386
|
continue;
|
|
6080
6387
|
}
|
|
6081
|
-
const
|
|
6388
|
+
const nameKey2 = `${filePath}#${localName}`;
|
|
6082
6389
|
const moduleText = importedModuleText(
|
|
6083
6390
|
binding.module,
|
|
6084
6391
|
binding.relativeLevel
|
|
@@ -6094,16 +6401,16 @@ function emitModuleImportFacts(db, filePath, module, resolverOptions) {
|
|
|
6094
6401
|
resolution.status === "resolved" ? "resolved" : resolution.reason
|
|
6095
6402
|
]);
|
|
6096
6403
|
const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
|
|
6097
|
-
db.add("pyImportedName", [
|
|
6404
|
+
db.add("pyImportedName", [nameKey2, moduleText, importedName]);
|
|
6098
6405
|
if (scope.kind === "module") {
|
|
6099
|
-
db.add("exportsAs", [filePath, localName,
|
|
6406
|
+
db.add("exportsAs", [filePath, localName, nameKey2]);
|
|
6100
6407
|
}
|
|
6101
6408
|
if (resolution.status === "resolved") {
|
|
6102
6409
|
db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
|
|
6103
|
-
db.add("imports", [
|
|
6410
|
+
db.add("imports", [nameKey2, resolution.file, importedName]);
|
|
6104
6411
|
continue;
|
|
6105
6412
|
}
|
|
6106
|
-
db.add("imports", [
|
|
6413
|
+
db.add("imports", [nameKey2, moduleText, importedName]);
|
|
6107
6414
|
}
|
|
6108
6415
|
}
|
|
6109
6416
|
for (const openModule of module.openImports) {
|
|
@@ -6274,7 +6581,7 @@ import {
|
|
|
6274
6581
|
unfollowedCallGap,
|
|
6275
6582
|
worthRecording
|
|
6276
6583
|
} from "@suss/behavioral-ir";
|
|
6277
|
-
import { Database, evaluate
|
|
6584
|
+
import { Database, evaluate, lit, rule, variable as v } from "@suss/datalog";
|
|
6278
6585
|
import {
|
|
6279
6586
|
assembleSummary,
|
|
6280
6587
|
SKIP_CHILDREN as SKIP_CHILDREN2,
|
|
@@ -6282,251 +6589,227 @@ import {
|
|
|
6282
6589
|
} from "@suss/extractor";
|
|
6283
6590
|
|
|
6284
6591
|
// src/reach/resolveCallee.ts
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6592
|
+
import {
|
|
6593
|
+
calleeOutcomeOf,
|
|
6594
|
+
calleeOutcomes,
|
|
6595
|
+
writtenSourcesOf
|
|
6596
|
+
} from "@suss/resolution";
|
|
6597
|
+
var STOP_FOR = {
|
|
6598
|
+
severalSources: "multipleSources",
|
|
6599
|
+
outsideRun: "outsideRun",
|
|
6600
|
+
unsettled: "unsettledValue",
|
|
6601
|
+
undeclared: "noDeclaration"
|
|
6289
6602
|
};
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6603
|
+
var NO_DECLARATION = {
|
|
6604
|
+
kind: "stopped",
|
|
6605
|
+
reason: "noDeclaration"
|
|
6606
|
+
};
|
|
6607
|
+
function calleeSpellings(calls, ctx) {
|
|
6608
|
+
const spellingOf = /* @__PURE__ */ new Map();
|
|
6609
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6610
|
+
for (const { call, site } of calls) {
|
|
6611
|
+
const spelling = spellingFor(call, site, ctx);
|
|
6612
|
+
spellingOf.set(call.id, spelling);
|
|
6613
|
+
if (spelling.kind === "key") {
|
|
6614
|
+
keys.add(spelling.key);
|
|
6615
|
+
}
|
|
6294
6616
|
}
|
|
6295
|
-
return
|
|
6617
|
+
return { spellingOf, outcomes: calleeOutcomes(ctx.facts, [...keys]) };
|
|
6296
6618
|
}
|
|
6297
|
-
function
|
|
6298
|
-
|
|
6299
|
-
|
|
6619
|
+
function resolveCallee2(call, site, ctx, read) {
|
|
6620
|
+
const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site, ctx);
|
|
6621
|
+
if (spelling.kind === "stopped") {
|
|
6622
|
+
return spelling;
|
|
6300
6623
|
}
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6624
|
+
const outcome = read?.outcomes.get(spelling.key) ?? calleeOutcomeOf(ctx.facts, spelling.key);
|
|
6625
|
+
return asCallee(outcome, site.owner, ctx);
|
|
6626
|
+
}
|
|
6627
|
+
function functionNamed(nameKey2, ctx) {
|
|
6628
|
+
if (assignedElsewhere(ctx.facts, nameKey2)) {
|
|
6303
6629
|
return null;
|
|
6304
6630
|
}
|
|
6305
|
-
const
|
|
6306
|
-
return
|
|
6631
|
+
const outcome = calleeOutcomeOf(ctx.facts, nameKey2);
|
|
6632
|
+
return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
|
|
6307
6633
|
}
|
|
6308
|
-
function
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
if (resolved.kind === "class") {
|
|
6313
|
-
return asCallee(
|
|
6314
|
-
memberOfClass(resolved.file, resolved.node, "__init__", ctx),
|
|
6315
|
-
ctx
|
|
6316
|
-
);
|
|
6317
|
-
}
|
|
6318
|
-
if (resolved.kind === "stop") {
|
|
6319
|
-
return { kind: "stopped", reason: resolved.reason };
|
|
6320
|
-
}
|
|
6321
|
-
if (resolved.kind === "package") {
|
|
6322
|
-
return { kind: "stopped", reason: "outsideRun" };
|
|
6323
|
-
}
|
|
6324
|
-
return { kind: "stopped", reason: "noDeclaration" };
|
|
6634
|
+
function assignedElsewhere(facts, nameKey2) {
|
|
6635
|
+
return writtenSourcesOf(facts, nameKey2).some(
|
|
6636
|
+
(source) => !facts.has("func", [source])
|
|
6637
|
+
);
|
|
6325
6638
|
}
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
const object = field(node, "object");
|
|
6330
|
-
const attribute = field(node, "attribute");
|
|
6331
|
-
if (object === null || attribute === null) {
|
|
6332
|
-
return stop("noDeclaration");
|
|
6333
|
-
}
|
|
6334
|
-
return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
|
|
6335
|
-
},
|
|
6336
|
-
call: (node, site, ctx) => {
|
|
6337
|
-
const callee = field(node, "function");
|
|
6338
|
-
return callee === null ? stop("noDeclaration") : instanceBuiltBy(resolveExpression(callee, site, ctx), "noDeclaration");
|
|
6339
|
-
},
|
|
6340
|
-
parenthesized_expression: (node, site, ctx) => {
|
|
6341
|
-
const inner = node.namedChildren[0];
|
|
6342
|
-
return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
|
|
6639
|
+
function readThrough(node) {
|
|
6640
|
+
if (node?.type !== "parenthesized_expression") {
|
|
6641
|
+
return node;
|
|
6343
6642
|
}
|
|
6344
|
-
|
|
6345
|
-
function resolveExpression(node, site, ctx) {
|
|
6346
|
-
const resolver = EXPRESSION_RESOLVERS[node.type];
|
|
6347
|
-
return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
|
|
6643
|
+
return readThrough(children(node)[0] ?? null);
|
|
6348
6644
|
}
|
|
6349
|
-
function
|
|
6350
|
-
|
|
6351
|
-
|
|
6645
|
+
function spellingFor(call, site, ctx) {
|
|
6646
|
+
const callee = readThrough(field(call, "function"));
|
|
6647
|
+
if (callee === null) {
|
|
6648
|
+
return { kind: "stopped", reason: "noDeclaration" };
|
|
6352
6649
|
}
|
|
6353
|
-
return
|
|
6650
|
+
return throughModules(callee, site, ctx) ?? {
|
|
6651
|
+
kind: "key",
|
|
6652
|
+
key: readKey(site.file.file, callee, enclosingFunction(callee))
|
|
6653
|
+
};
|
|
6354
6654
|
}
|
|
6355
|
-
function
|
|
6356
|
-
if (
|
|
6357
|
-
return
|
|
6655
|
+
function asCallee(outcome, owner, ctx) {
|
|
6656
|
+
if (outcome.kind === "function") {
|
|
6657
|
+
return functionCallee(outcome.key, ctx);
|
|
6358
6658
|
}
|
|
6359
|
-
if (
|
|
6360
|
-
return
|
|
6659
|
+
if (outcome.kind === "object") {
|
|
6660
|
+
return constructorOf(outcome.key, ctx);
|
|
6361
6661
|
}
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6662
|
+
if (outcome.kind === "callerSupplied") {
|
|
6663
|
+
return outcome.key.startsWith(`${owner}#`) ? { kind: "stopped", reason: "callerSupplied" } : { kind: "stopped", reason: "unsettledValue" };
|
|
6365
6664
|
}
|
|
6366
|
-
return
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6665
|
+
return {
|
|
6666
|
+
kind: "stopped",
|
|
6667
|
+
reason: STOP_FOR[outcome.kind] ?? "noDeclaration"
|
|
6668
|
+
};
|
|
6370
6669
|
}
|
|
6371
|
-
function
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
return stop("noDeclaration");
|
|
6670
|
+
function functionCallee(key, ctx) {
|
|
6671
|
+
if (ctx.definitions.get(key)?.type === "lambda") {
|
|
6672
|
+
return { kind: "stopped", reason: "unsettledValue" };
|
|
6375
6673
|
}
|
|
6376
|
-
|
|
6674
|
+
const target = functionAt(key, ctx);
|
|
6675
|
+
return target === null ? NO_DECLARATION : { kind: "followed", target };
|
|
6377
6676
|
}
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
}),
|
|
6383
|
-
classDef: (binding, file) => ({ kind: "class", file, node: binding.node }),
|
|
6384
|
-
parameter: () => stop("callerSupplied"),
|
|
6385
|
-
// `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
|
|
6386
|
-
import: (binding, file, _scope, ctx) => moduleNamed(
|
|
6387
|
-
file,
|
|
6388
|
-
binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
|
|
6389
|
-
0,
|
|
6390
|
-
ctx
|
|
6391
|
-
),
|
|
6392
|
-
importFrom: (binding, file, _scope, ctx, visited) => memberOf(
|
|
6393
|
-
moduleNamed(file, binding.module, binding.relativeLevel, ctx),
|
|
6394
|
-
binding.importedName,
|
|
6395
|
-
ctx,
|
|
6396
|
-
visited
|
|
6397
|
-
),
|
|
6398
|
-
// `run = load` is an alias worth one hop; anything else is a value.
|
|
6399
|
-
assignment: (binding, file, scope, ctx, visited) => aliasedValue(binding.value, file, scope, ctx, visited),
|
|
6400
|
-
global: () => stop("unsettledValue"),
|
|
6401
|
-
nonlocal: () => stop("unsettledValue")
|
|
6402
|
-
};
|
|
6403
|
-
function resolveBinding(binding, file, scope, ctx, visited) {
|
|
6404
|
-
const resolver = BINDING_RESOLVERS[binding.kind];
|
|
6405
|
-
return resolver(binding, file, scope, ctx, visited);
|
|
6406
|
-
}
|
|
6407
|
-
function aliasedValue(value, file, scope, ctx, visited) {
|
|
6408
|
-
if (value === null) {
|
|
6409
|
-
return stop("unsettledValue");
|
|
6410
|
-
}
|
|
6411
|
-
if (value.type === "identifier") {
|
|
6412
|
-
const binding = resolveName(scope, value.text);
|
|
6413
|
-
if (binding === null || binding.kind === "assignment") {
|
|
6414
|
-
return stop("unsettledValue");
|
|
6415
|
-
}
|
|
6416
|
-
return resolveBinding(binding, file, scope, ctx, visited);
|
|
6417
|
-
}
|
|
6418
|
-
if (value.type === "call") {
|
|
6419
|
-
const callee = field(value, "function");
|
|
6420
|
-
return callee === null ? stop("unsettledValue") : instanceBuiltBy(
|
|
6421
|
-
aliasedValue(callee, file, scope, ctx, visited),
|
|
6422
|
-
"unsettledValue"
|
|
6423
|
-
);
|
|
6677
|
+
function constructorOf(classKey, ctx) {
|
|
6678
|
+
const declared = ctx.facts.lookup("holdsProperty", 0, classKey).find((row) => String(row[1]) === "__init__");
|
|
6679
|
+
if (declared === void 0) {
|
|
6680
|
+
return NO_DECLARATION;
|
|
6424
6681
|
}
|
|
6425
|
-
|
|
6426
|
-
const object = field(value, "object");
|
|
6427
|
-
const attribute = field(value, "attribute");
|
|
6428
|
-
if (object === null || attribute === null) {
|
|
6429
|
-
return stop("unsettledValue");
|
|
6430
|
-
}
|
|
6431
|
-
return memberOf(
|
|
6432
|
-
aliasedValue(object, file, scope, ctx, visited),
|
|
6433
|
-
attribute.text,
|
|
6434
|
-
ctx
|
|
6435
|
-
);
|
|
6436
|
-
}
|
|
6437
|
-
return stop("unsettledValue");
|
|
6682
|
+
return functionCallee(String(declared[2]), ctx);
|
|
6438
6683
|
}
|
|
6439
|
-
function
|
|
6684
|
+
function functionAt(key, ctx) {
|
|
6685
|
+
const node = ctx.definitions.get(key);
|
|
6686
|
+
const file = ctx.filesByPath.get(key.slice(0, key.lastIndexOf(":")));
|
|
6687
|
+
if (node === void 0 || file === void 0) {
|
|
6688
|
+
return null;
|
|
6689
|
+
}
|
|
6440
6690
|
const name = field(node, "name")?.text ?? "<anon>";
|
|
6441
|
-
const
|
|
6691
|
+
const owner = ownerClassOf(node);
|
|
6692
|
+
const ownerName = owner === null ? void 0 : field(owner, "name")?.text;
|
|
6442
6693
|
return {
|
|
6443
6694
|
file,
|
|
6444
6695
|
node,
|
|
6445
6696
|
name,
|
|
6446
|
-
exportPath: ownerName ===
|
|
6697
|
+
exportPath: ownerName === void 0 ? [name] : [ownerName, name]
|
|
6447
6698
|
};
|
|
6448
6699
|
}
|
|
6449
|
-
function
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6700
|
+
function ownerClassOf(node) {
|
|
6701
|
+
for (let up = node.parent; up !== null; up = up.parent) {
|
|
6702
|
+
if (isFunction(up)) {
|
|
6703
|
+
return null;
|
|
6704
|
+
}
|
|
6705
|
+
if (up.type === "class_definition") {
|
|
6706
|
+
return up;
|
|
6707
|
+
}
|
|
6457
6708
|
}
|
|
6458
|
-
|
|
6459
|
-
return file === void 0 ? stop("outsideRun") : { kind: "module", file };
|
|
6709
|
+
return null;
|
|
6460
6710
|
}
|
|
6461
|
-
function
|
|
6462
|
-
if (
|
|
6463
|
-
return
|
|
6711
|
+
function throughModules(callee, site, ctx) {
|
|
6712
|
+
if (callee.type === "identifier") {
|
|
6713
|
+
return resolveName(site.scope, callee.text) === null ? openImportMember(site.file, callee.text, ctx, /* @__PURE__ */ new Set()) : null;
|
|
6464
6714
|
}
|
|
6465
|
-
if (
|
|
6466
|
-
return
|
|
6467
|
-
base.from,
|
|
6468
|
-
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
6469
|
-
base.relativeLevel,
|
|
6470
|
-
ctx
|
|
6471
|
-
);
|
|
6715
|
+
if (callee.type !== "attribute") {
|
|
6716
|
+
return null;
|
|
6472
6717
|
}
|
|
6473
|
-
|
|
6474
|
-
|
|
6718
|
+
const object = field(callee, "object");
|
|
6719
|
+
const attribute = field(callee, "attribute");
|
|
6720
|
+
if (object === null || attribute === null) {
|
|
6721
|
+
return null;
|
|
6475
6722
|
}
|
|
6476
|
-
|
|
6477
|
-
|
|
6723
|
+
const base = moduleAt(object, site, ctx);
|
|
6724
|
+
return base === null ? null : memberKey(base, attribute.text, ctx);
|
|
6725
|
+
}
|
|
6726
|
+
function openImportMember(file, name, ctx, visited) {
|
|
6727
|
+
if (visited.has(file.file)) {
|
|
6728
|
+
return null;
|
|
6729
|
+
}
|
|
6730
|
+
visited.add(file.file);
|
|
6731
|
+
const found = /* @__PURE__ */ new Set();
|
|
6732
|
+
for (const spec of file.module.openImports) {
|
|
6733
|
+
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6734
|
+
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6735
|
+
if (opened === null || opened.kind !== "module") {
|
|
6736
|
+
continue;
|
|
6737
|
+
}
|
|
6738
|
+
const member = opened.file.module.moduleScope.bindings.has(name) ? { kind: "key", key: `${opened.file.file}#${name}` } : openImportMember(opened.file, name, ctx, visited);
|
|
6739
|
+
if (member?.kind === "key") {
|
|
6740
|
+
found.add(member.key);
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
if (found.size > 1) {
|
|
6744
|
+
return { kind: "stopped", reason: "multipleSources" };
|
|
6478
6745
|
}
|
|
6479
|
-
|
|
6746
|
+
const only = [...found][0];
|
|
6747
|
+
return only === void 0 ? null : { kind: "key", key: only };
|
|
6480
6748
|
}
|
|
6481
|
-
function
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
if (binding === void 0) {
|
|
6485
|
-
return stop("noDeclaration");
|
|
6749
|
+
function moduleAt(node, site, ctx) {
|
|
6750
|
+
if (node.type === "identifier") {
|
|
6751
|
+
return importedModule(node.text, site, ctx);
|
|
6486
6752
|
}
|
|
6487
|
-
if (
|
|
6488
|
-
return
|
|
6489
|
-
kind: "function",
|
|
6490
|
-
target: reachedFunction(file, binding.node, classNode)
|
|
6491
|
-
};
|
|
6753
|
+
if (node.type !== "attribute") {
|
|
6754
|
+
return null;
|
|
6492
6755
|
}
|
|
6493
|
-
|
|
6756
|
+
const object = field(node, "object");
|
|
6757
|
+
const attribute = field(node, "attribute");
|
|
6758
|
+
if (object === null || attribute === null) {
|
|
6759
|
+
return null;
|
|
6760
|
+
}
|
|
6761
|
+
const base = moduleAt(object, site, ctx);
|
|
6762
|
+
return base === null ? null : submoduleOf(base, attribute.text, ctx);
|
|
6494
6763
|
}
|
|
6495
|
-
function
|
|
6496
|
-
const
|
|
6497
|
-
if (
|
|
6498
|
-
|
|
6764
|
+
function importedModule(name, site, ctx) {
|
|
6765
|
+
const binding = resolveName(site.scope, name);
|
|
6766
|
+
if (binding?.kind === "import") {
|
|
6767
|
+
const head = binding.module.split(".")[0];
|
|
6768
|
+
const dotted = binding.localName === head ? binding.localName : binding.module;
|
|
6769
|
+
return moduleNamed(site.file, dotted, 0, ctx);
|
|
6499
6770
|
}
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
if (binding !== void 0) {
|
|
6503
|
-
return resolveBinding(binding, file, file.module.moduleScope, ctx, visited);
|
|
6771
|
+
if (binding?.kind !== "importFrom") {
|
|
6772
|
+
return null;
|
|
6504
6773
|
}
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6774
|
+
const from = moduleNamed(
|
|
6775
|
+
site.file,
|
|
6776
|
+
binding.module,
|
|
6777
|
+
binding.relativeLevel,
|
|
6778
|
+
ctx
|
|
6779
|
+
);
|
|
6780
|
+
return from === null ? null : submoduleOf(from, binding.importedName, ctx);
|
|
6781
|
+
}
|
|
6782
|
+
function submoduleOf(base, name, ctx) {
|
|
6783
|
+
if (base.kind === "package") {
|
|
6784
|
+
return moduleNamed(
|
|
6785
|
+
base.from,
|
|
6786
|
+
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
6787
|
+
base.relativeLevel,
|
|
6788
|
+
ctx
|
|
6789
|
+
);
|
|
6510
6790
|
}
|
|
6511
|
-
return
|
|
6791
|
+
return base.file.file.endsWith("__init__.py") ? moduleNamed(base.file, name, 1, ctx) : null;
|
|
6512
6792
|
}
|
|
6513
|
-
function
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6517
|
-
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6518
|
-
if (opened.kind !== "module") {
|
|
6519
|
-
continue;
|
|
6520
|
-
}
|
|
6521
|
-
const member = memberOfModule(opened.file, name, ctx, visited);
|
|
6522
|
-
if (member.kind !== "stop") {
|
|
6523
|
-
found.push(member);
|
|
6524
|
-
}
|
|
6793
|
+
function memberKey(base, name, ctx) {
|
|
6794
|
+
if (base.kind === "package") {
|
|
6795
|
+
return null;
|
|
6525
6796
|
}
|
|
6526
|
-
if (
|
|
6527
|
-
return
|
|
6797
|
+
if (base.file.module.moduleScope.bindings.has(name)) {
|
|
6798
|
+
return { kind: "key", key: `${base.file.file}#${name}` };
|
|
6528
6799
|
}
|
|
6529
|
-
return
|
|
6800
|
+
return openImportMember(base.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6801
|
+
}
|
|
6802
|
+
function moduleNamed(from, module, relativeLevel, ctx) {
|
|
6803
|
+
const resolution = resolveModule(
|
|
6804
|
+
from.file,
|
|
6805
|
+
{ module, relativeLevel },
|
|
6806
|
+
{ roots: ctx.roots }
|
|
6807
|
+
);
|
|
6808
|
+
if (resolution.status !== "resolved") {
|
|
6809
|
+
return resolution.reason === "external" ? { kind: "package", dotted: module, relativeLevel, from } : null;
|
|
6810
|
+
}
|
|
6811
|
+
const file = ctx.filesByPath.get(resolution.file);
|
|
6812
|
+
return file === void 0 ? null : { kind: "module", file };
|
|
6530
6813
|
}
|
|
6531
6814
|
|
|
6532
6815
|
// src/reach/closure.ts
|
|
@@ -6541,7 +6824,9 @@ var REACHABLE_RULES = [
|
|
|
6541
6824
|
function reachedFunctions(seeds, options) {
|
|
6542
6825
|
const ctx = {
|
|
6543
6826
|
filesByPath: new Map(options.files.map((file) => [file.file, file])),
|
|
6544
|
-
roots: options.roots
|
|
6827
|
+
roots: options.roots,
|
|
6828
|
+
facts: options.facts,
|
|
6829
|
+
definitions: options.definitions
|
|
6545
6830
|
};
|
|
6546
6831
|
const db = new Database();
|
|
6547
6832
|
const functionByKey = /* @__PURE__ */ new Map();
|
|
@@ -6563,21 +6848,26 @@ function reachedFunctions(seeds, options) {
|
|
|
6563
6848
|
db.add("entry", [seed.key]);
|
|
6564
6849
|
}
|
|
6565
6850
|
for (; ; ) {
|
|
6566
|
-
|
|
6851
|
+
evaluate(db, REACHABLE_RULES);
|
|
6567
6852
|
const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
|
|
6568
6853
|
if (frontier.length === 0) {
|
|
6569
6854
|
break;
|
|
6570
6855
|
}
|
|
6571
|
-
|
|
6856
|
+
const bodies = frontier.flatMap((key) => {
|
|
6572
6857
|
scanned.add(key);
|
|
6573
6858
|
const source = functionByKey.get(key);
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6859
|
+
return source === void 0 ? [] : [{ key, source, ...bodyOf(source) }];
|
|
6860
|
+
});
|
|
6861
|
+
const spellings = calleeSpellings(
|
|
6862
|
+
bodies.flatMap((body) => body.written),
|
|
6863
|
+
ctx
|
|
6864
|
+
);
|
|
6865
|
+
for (const { key, source, body, written } of bodies) {
|
|
6577
6866
|
const scan = scanBody(
|
|
6578
6867
|
source,
|
|
6579
6868
|
ctx,
|
|
6580
|
-
recognizedCallIds(source.node, options.storageFor(source.file))
|
|
6869
|
+
recognizedCallIds(source.node, options.storageFor(source.file)),
|
|
6870
|
+
{ body, written, spellings }
|
|
6581
6871
|
);
|
|
6582
6872
|
if (scan.stops.length > 0) {
|
|
6583
6873
|
stopsByKey.set(key, scan.stops);
|
|
@@ -6643,7 +6933,18 @@ var EMPTY_SCAN = {
|
|
|
6643
6933
|
parameterCalls: [],
|
|
6644
6934
|
passedPositions: /* @__PURE__ */ new Set()
|
|
6645
6935
|
};
|
|
6646
|
-
function
|
|
6936
|
+
function bodyOf(source) {
|
|
6937
|
+
const { file, node } = source;
|
|
6938
|
+
const body = field(node, "body");
|
|
6939
|
+
if (body === null) {
|
|
6940
|
+
return { body, written: [] };
|
|
6941
|
+
}
|
|
6942
|
+
return {
|
|
6943
|
+
body,
|
|
6944
|
+
written: callsWritten(file, body, scopeAt2(file, node), keyOf(source))
|
|
6945
|
+
};
|
|
6946
|
+
}
|
|
6947
|
+
function scanBody(source, ctx, recognized, read) {
|
|
6647
6948
|
const followed = [];
|
|
6648
6949
|
const stops = [];
|
|
6649
6950
|
const placements = new TargetPlacements();
|
|
@@ -6652,12 +6953,12 @@ function scanBody(source, ctx, recognized) {
|
|
|
6652
6953
|
const seen = /* @__PURE__ */ new Set();
|
|
6653
6954
|
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
6654
6955
|
const { file, node } = source;
|
|
6655
|
-
const
|
|
6656
|
-
if (body === null) {
|
|
6956
|
+
const { written, spellings } = read;
|
|
6957
|
+
if (read.body === null) {
|
|
6657
6958
|
return EMPTY_SCAN;
|
|
6658
6959
|
}
|
|
6659
6960
|
const ownParameters = callParameterNames(node, source.exportPath.length > 1);
|
|
6660
|
-
const recordPassedArgs = (call, callee, calleeKey
|
|
6961
|
+
const recordPassedArgs = (call, callee, calleeKey) => {
|
|
6661
6962
|
const args = field(call, "arguments");
|
|
6662
6963
|
if (args === null) {
|
|
6663
6964
|
return;
|
|
@@ -6666,9 +6967,8 @@ function scanBody(source, ctx, recognized) {
|
|
|
6666
6967
|
if (arg === null || arg.type !== "identifier") {
|
|
6667
6968
|
return;
|
|
6668
6969
|
}
|
|
6669
|
-
const resolved =
|
|
6670
|
-
|
|
6671
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6970
|
+
const resolved = functionNamed(
|
|
6971
|
+
readKey(file.file, arg, enclosingFunction(arg)),
|
|
6672
6972
|
ctx
|
|
6673
6973
|
);
|
|
6674
6974
|
if (resolved === null) {
|
|
@@ -6688,13 +6988,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6688
6988
|
}
|
|
6689
6989
|
});
|
|
6690
6990
|
};
|
|
6691
|
-
const record = (call,
|
|
6991
|
+
const record = (call, site) => {
|
|
6692
6992
|
const callee = calleeText(call);
|
|
6693
|
-
const outcome = resolveCallee2(
|
|
6694
|
-
call,
|
|
6695
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6696
|
-
ctx
|
|
6697
|
-
);
|
|
6993
|
+
const outcome = resolveCallee2(call, site, ctx, spellings);
|
|
6698
6994
|
const placed = outcome.kind === "followed" ? {
|
|
6699
6995
|
file: outcome.target.file.displayPath,
|
|
6700
6996
|
span: spanOf(outcome.target.node)
|
|
@@ -6703,8 +6999,7 @@ function scanBody(source, ctx, recognized) {
|
|
|
6703
6999
|
recordPassedArgs(
|
|
6704
7000
|
call,
|
|
6705
7001
|
callee,
|
|
6706
|
-
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6707
|
-
where
|
|
7002
|
+
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6708
7003
|
);
|
|
6709
7004
|
if (outcome.kind === "stopped") {
|
|
6710
7005
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
@@ -6727,32 +7022,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6727
7022
|
followed.push(outcome.target);
|
|
6728
7023
|
}
|
|
6729
7024
|
};
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
{
|
|
6734
|
-
at: (child, where) => {
|
|
6735
|
-
if (child.type === "call") {
|
|
6736
|
-
record(child, where);
|
|
6737
|
-
}
|
|
6738
|
-
},
|
|
6739
|
-
into: (child, where) => {
|
|
6740
|
-
if (child.type === "function_definition") {
|
|
6741
|
-
return SKIP_CHILDREN2;
|
|
6742
|
-
}
|
|
6743
|
-
if (child.type === "lambda") {
|
|
6744
|
-
return {
|
|
6745
|
-
scope: where.scope,
|
|
6746
|
-
rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
|
|
6747
|
-
};
|
|
6748
|
-
}
|
|
6749
|
-
return {
|
|
6750
|
-
scope: file.module.scopeFor.get(child.id) ?? where.scope,
|
|
6751
|
-
rebound: where.rebound
|
|
6752
|
-
};
|
|
6753
|
-
}
|
|
6754
|
-
}
|
|
6755
|
-
);
|
|
7025
|
+
for (const { call, site } of written) {
|
|
7026
|
+
record(call, site);
|
|
7027
|
+
}
|
|
6756
7028
|
return {
|
|
6757
7029
|
followed,
|
|
6758
7030
|
stops,
|
|
@@ -6762,6 +7034,18 @@ function scanBody(source, ctx, recognized) {
|
|
|
6762
7034
|
passedPositions
|
|
6763
7035
|
};
|
|
6764
7036
|
}
|
|
7037
|
+
function callsWritten(file, body, outer, owner) {
|
|
7038
|
+
const found = [];
|
|
7039
|
+
walkDescendants3(body, outer, {
|
|
7040
|
+
at: (child, scope) => {
|
|
7041
|
+
if (child.type === "call") {
|
|
7042
|
+
found.push({ call: child, site: { file, scope, owner } });
|
|
7043
|
+
}
|
|
7044
|
+
},
|
|
7045
|
+
into: (child, scope) => child.type === "function_definition" ? SKIP_CHILDREN2 : file.module.scopeFor.get(child.id) ?? scope
|
|
7046
|
+
});
|
|
7047
|
+
return found;
|
|
7048
|
+
}
|
|
6765
7049
|
function scopeAt2(file, node) {
|
|
6766
7050
|
let current = node;
|
|
6767
7051
|
while (current !== null) {
|
|
@@ -6773,40 +7057,6 @@ function scopeAt2(file, node) {
|
|
|
6773
7057
|
}
|
|
6774
7058
|
return file.module.moduleScope;
|
|
6775
7059
|
}
|
|
6776
|
-
var REBINDING_FIELDS = {
|
|
6777
|
-
assignment: "left",
|
|
6778
|
-
augmented_assignment: "left",
|
|
6779
|
-
named_expression: "name",
|
|
6780
|
-
for_statement: "left",
|
|
6781
|
-
for_in_clause: "left",
|
|
6782
|
-
as_pattern: "alias"
|
|
6783
|
-
};
|
|
6784
|
-
function reboundNames(body) {
|
|
6785
|
-
const names = /* @__PURE__ */ new Set();
|
|
6786
|
-
walkDescendants3(body, null, {
|
|
6787
|
-
at: (node) => {
|
|
6788
|
-
const target = REBINDING_FIELDS[node.type];
|
|
6789
|
-
if (target === void 0 || boundByBinder(node, body)) {
|
|
6790
|
-
return;
|
|
6791
|
-
}
|
|
6792
|
-
const written = field(node, target);
|
|
6793
|
-
if (written !== null) {
|
|
6794
|
-
for (const name of identifiersUnder(written)) {
|
|
6795
|
-
names.add(name);
|
|
6796
|
-
}
|
|
6797
|
-
}
|
|
6798
|
-
},
|
|
6799
|
-
into: (node) => node.type === "function_definition" ? SKIP_CHILDREN2 : null
|
|
6800
|
-
});
|
|
6801
|
-
return names;
|
|
6802
|
-
}
|
|
6803
|
-
function boundByBinder(node, body) {
|
|
6804
|
-
if (node.type !== "assignment") {
|
|
6805
|
-
return false;
|
|
6806
|
-
}
|
|
6807
|
-
const statement = node.parent;
|
|
6808
|
-
return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
|
|
6809
|
-
}
|
|
6810
7060
|
function identifiersUnder(node, found = []) {
|
|
6811
7061
|
if (node.type === "identifier") {
|
|
6812
7062
|
found.push(node.text);
|
|
@@ -6819,10 +7069,6 @@ function identifiersUnder(node, found = []) {
|
|
|
6819
7069
|
}
|
|
6820
7070
|
return found;
|
|
6821
7071
|
}
|
|
6822
|
-
function lambdaParameters(lambda) {
|
|
6823
|
-
const parameters = field(lambda, "parameters");
|
|
6824
|
-
return parameters === null ? [] : identifiersUnder(parameters);
|
|
6825
|
-
}
|
|
6826
7072
|
function libraryUnit(target, options) {
|
|
6827
7073
|
const { file, node, name, exportPath } = target;
|
|
6828
7074
|
const body = field(node, "body");
|
|
@@ -6973,6 +7219,18 @@ var PythonWrapperIndex = class {
|
|
|
6973
7219
|
ownRoutes = /* @__PURE__ */ new Map();
|
|
6974
7220
|
unitsByKey = /* @__PURE__ */ new Map();
|
|
6975
7221
|
unitsByFile = /* @__PURE__ */ new Map();
|
|
7222
|
+
/** The project function a module-level name in this file refers to, or null for anything else. */
|
|
7223
|
+
functionCalled(file, name) {
|
|
7224
|
+
if (this.options.facts === void 0) {
|
|
7225
|
+
return null;
|
|
7226
|
+
}
|
|
7227
|
+
return functionNamed(nameKeyIn(file, null, name), {
|
|
7228
|
+
filesByPath: this.filesByPath,
|
|
7229
|
+
roots: this.options.roots,
|
|
7230
|
+
facts: this.options.facts,
|
|
7231
|
+
definitions: this.options.definitions
|
|
7232
|
+
});
|
|
7233
|
+
}
|
|
6976
7234
|
register(covers, target, declared) {
|
|
6977
7235
|
const registered = this.registered(target, declared);
|
|
6978
7236
|
const table = covers.kind === "everyRoute" ? this.everyRoute : this.ownRoutes;
|
|
@@ -7019,20 +7277,12 @@ var PythonWrapperIndex = class {
|
|
|
7019
7277
|
if (declared.form.type !== "dependency") {
|
|
7020
7278
|
continue;
|
|
7021
7279
|
}
|
|
7022
|
-
const site = {
|
|
7023
|
-
file,
|
|
7024
|
-
scope: query.module.moduleScope,
|
|
7025
|
-
rebound: /* @__PURE__ */ new Set()
|
|
7026
|
-
};
|
|
7027
7280
|
const targets = [
|
|
7028
7281
|
...decoratorDependencies(query.classification, declared.form),
|
|
7029
7282
|
...parameterDependencies(query.definitionNode, declared.form)
|
|
7030
7283
|
];
|
|
7031
7284
|
for (const name of targets) {
|
|
7032
|
-
const target =
|
|
7033
|
-
filesByPath: this.filesByPath,
|
|
7034
|
-
roots: this.options.roots
|
|
7035
|
-
});
|
|
7285
|
+
const target = this.functionCalled(file.file, name);
|
|
7036
7286
|
if (target !== null) {
|
|
7037
7287
|
found.push(this.registered(target, declared));
|
|
7038
7288
|
}
|
|
@@ -7174,12 +7424,8 @@ function registerConstructorDependencies(file, declared, form, index) {
|
|
|
7174
7424
|
if (listed === void 0) {
|
|
7175
7425
|
continue;
|
|
7176
7426
|
}
|
|
7177
|
-
const site = { file, scope, rebound: /* @__PURE__ */ new Set() };
|
|
7178
7427
|
for (const name of dependencyNamesIn(listed.node, form)) {
|
|
7179
|
-
const target =
|
|
7180
|
-
filesByPath: index.filesByPath,
|
|
7181
|
-
roots: index.options.roots
|
|
7182
|
-
});
|
|
7428
|
+
const target = index.functionCalled(file.file, name);
|
|
7183
7429
|
if (target !== null) {
|
|
7184
7430
|
index.register(
|
|
7185
7431
|
coverageOf(match, file, declared.pack),
|
|
@@ -7419,6 +7665,31 @@ function reportUnresolvedProjectModules(options, db) {
|
|
|
7419
7665
|
}
|
|
7420
7666
|
}
|
|
7421
7667
|
}
|
|
7668
|
+
function emitModelQueryFacts(db, packs) {
|
|
7669
|
+
for (const pack of packs) {
|
|
7670
|
+
for (const model of pack.models ?? []) {
|
|
7671
|
+
for (const base of model.baseNames) {
|
|
7672
|
+
for (const method of model.givesBack) {
|
|
7673
|
+
db.add("givesBackOne", [base, method]);
|
|
7674
|
+
}
|
|
7675
|
+
for (const entry of model.entryMethods) {
|
|
7676
|
+
db.add("givesBackOneOfArgument", [
|
|
7677
|
+
base,
|
|
7678
|
+
entry.method,
|
|
7679
|
+
String(entry.argument)
|
|
7680
|
+
]);
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
for (const entry of model.entryFunctions) {
|
|
7684
|
+
db.add("givesBackOneOfImport", [
|
|
7685
|
+
entry.module,
|
|
7686
|
+
entry.name,
|
|
7687
|
+
String(entry.argument)
|
|
7688
|
+
]);
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7422
7693
|
async function extractPythonProject(options) {
|
|
7423
7694
|
const timer = options.onTiming !== void 0 ? createTimer() : noopTimer();
|
|
7424
7695
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
@@ -7466,8 +7737,9 @@ async function extractPythonProject(options) {
|
|
|
7466
7737
|
);
|
|
7467
7738
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
7468
7739
|
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
7740
|
+
const modelQueries = options.packs.flatMap((pack) => pack.models ?? []);
|
|
7469
7741
|
const discovers = options.packs.some((pack) => pack.discovery.length > 0);
|
|
7470
|
-
const needsValues = discovers || mountsRouters || storagePatterns.length > 0;
|
|
7742
|
+
const needsValues = discovers || mountsRouters || storagePatterns.length > 0 || modelQueries.length > 0;
|
|
7471
7743
|
const definitions = /* @__PURE__ */ new Map();
|
|
7472
7744
|
timer.time("discover", () => {
|
|
7473
7745
|
for (const { file, root, module: moduleBinding } of bound) {
|
|
@@ -7480,6 +7752,7 @@ async function extractPythonProject(options) {
|
|
|
7480
7752
|
if (needsValues) {
|
|
7481
7753
|
bindEvaluator(db, { files: bound, definitions });
|
|
7482
7754
|
}
|
|
7755
|
+
emitModelQueryFacts(db, options.packs);
|
|
7483
7756
|
});
|
|
7484
7757
|
reportUnresolvedProjectModules(options, db);
|
|
7485
7758
|
const couldMatch = timer.time(
|
|
@@ -7507,6 +7780,7 @@ async function extractPythonProject(options) {
|
|
|
7507
7780
|
packs: options.packs,
|
|
7508
7781
|
roots: options.roots,
|
|
7509
7782
|
facts: needsValues ? db : void 0,
|
|
7783
|
+
definitions,
|
|
7510
7784
|
storageFor
|
|
7511
7785
|
})
|
|
7512
7786
|
);
|
|
@@ -7590,7 +7864,9 @@ async function extractPythonProject(options) {
|
|
|
7590
7864
|
files: bound,
|
|
7591
7865
|
roots: options.roots,
|
|
7592
7866
|
gapHandling,
|
|
7593
|
-
storageFor
|
|
7867
|
+
storageFor,
|
|
7868
|
+
facts: db,
|
|
7869
|
+
definitions
|
|
7594
7870
|
})
|
|
7595
7871
|
);
|
|
7596
7872
|
for (const [key, owners] of summariesBySeed) {
|
|
@@ -7852,7 +8128,7 @@ var PythonWhySession = class {
|
|
|
7852
8128
|
explain(value, options = {}) {
|
|
7853
8129
|
return explainResolvedKey({
|
|
7854
8130
|
db: this.db,
|
|
7855
|
-
rules:
|
|
8131
|
+
rules: RESOLUTION_RULES,
|
|
7856
8132
|
key: readKey(value.file, value.node, enclosingFunction(value.node)),
|
|
7857
8133
|
locate: (key) => this.locate(key),
|
|
7858
8134
|
displayPath: (key) => this.displayPath(key),
|
|
@@ -7982,6 +8258,7 @@ export {
|
|
|
7982
8258
|
emitValueFacts,
|
|
7983
8259
|
extractPythonProject,
|
|
7984
8260
|
findPythonFiles,
|
|
8261
|
+
nameKeyIn,
|
|
7985
8262
|
nodeId,
|
|
7986
8263
|
objectReturnedBy,
|
|
7987
8264
|
parsePython,
|