@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.cjs
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
emitValueFacts: () => emitValueFacts,
|
|
44
44
|
extractPythonProject: () => extractPythonProject,
|
|
45
45
|
findPythonFiles: () => findPythonFiles,
|
|
46
|
+
nameKeyIn: () => nameKeyIn,
|
|
46
47
|
nodeId: () => nodeId,
|
|
47
48
|
objectReturnedBy: () => objectReturnedBy,
|
|
48
49
|
parsePython: () => parsePython,
|
|
@@ -90,6 +91,11 @@ function isType(node, ...types) {
|
|
|
90
91
|
function isFunction(node) {
|
|
91
92
|
return node.type === "function_definition" || node.type === "lambda";
|
|
92
93
|
}
|
|
94
|
+
var LATER_BODY_TYPES = /* @__PURE__ */ new Set([
|
|
95
|
+
"function_definition",
|
|
96
|
+
"lambda",
|
|
97
|
+
"class_definition"
|
|
98
|
+
]);
|
|
93
99
|
function enclosingFunction(node) {
|
|
94
100
|
let current = node.parent;
|
|
95
101
|
while (current !== null) {
|
|
@@ -1464,14 +1470,14 @@ function invocationEffects(definitionNode) {
|
|
|
1464
1470
|
if (calls.length === 0) {
|
|
1465
1471
|
return [];
|
|
1466
1472
|
}
|
|
1467
|
-
const
|
|
1473
|
+
const statementOf3 = /* @__PURE__ */ new Map();
|
|
1468
1474
|
const statements = [];
|
|
1469
1475
|
for (const call of calls) {
|
|
1470
1476
|
const statement = enclosingStatement(call, body);
|
|
1471
1477
|
if (statement === null) {
|
|
1472
1478
|
continue;
|
|
1473
1479
|
}
|
|
1474
|
-
|
|
1480
|
+
statementOf3.set(call.id, statement);
|
|
1475
1481
|
statements.push(statement);
|
|
1476
1482
|
}
|
|
1477
1483
|
const lowered = lowerPythonBody(body, statements);
|
|
@@ -1483,7 +1489,7 @@ function invocationEffects(definitionNode) {
|
|
|
1483
1489
|
statements
|
|
1484
1490
|
);
|
|
1485
1491
|
return calls.map((call) => {
|
|
1486
|
-
const statement =
|
|
1492
|
+
const statement = statementOf3.get(call.id);
|
|
1487
1493
|
const conditions = (0, import_extractor3.sharedGatingConditions)(
|
|
1488
1494
|
statement === void 0 ? void 0 : enumerated.byTerminal.get(statement),
|
|
1489
1495
|
predicateOf
|
|
@@ -1499,36 +1505,20 @@ function invocationEffects(definitionNode) {
|
|
|
1499
1505
|
}
|
|
1500
1506
|
|
|
1501
1507
|
// src/routers.ts
|
|
1502
|
-
var
|
|
1508
|
+
var import_resolution4 = require("@suss/resolution");
|
|
1503
1509
|
|
|
1504
1510
|
// src/facts/resolve.ts
|
|
1505
|
-
var import_datalog = require("@suss/datalog");
|
|
1506
1511
|
var import_resolution = require("@suss/resolution");
|
|
1507
|
-
var RESOLUTION_PROGRAM = (0, import_datalog.deriveOnDemand)(
|
|
1508
|
-
[...import_resolution.RESOLUTION_RULES, ...import_resolution.RESOLUTION_QUESTIONS],
|
|
1509
|
-
import_resolution.ANSWER_RELATIONS
|
|
1510
|
-
);
|
|
1511
1512
|
function resolveCalls(db, callKeys) {
|
|
1512
|
-
|
|
1513
|
-
const fresh = callKeys.filter((key) => !asked.has(key));
|
|
1514
|
-
if (fresh.length === 0) {
|
|
1515
|
-
return;
|
|
1516
|
-
}
|
|
1517
|
-
for (const callKey of fresh) {
|
|
1518
|
-
db.add("wanted", [callKey]);
|
|
1519
|
-
}
|
|
1520
|
-
(0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
|
|
1513
|
+
(0, import_resolution.askResolution)(db, callKeys);
|
|
1521
1514
|
}
|
|
1522
1515
|
function writtenValueOf(db, key) {
|
|
1523
1516
|
resolveCalls(db, [key]);
|
|
1524
1517
|
return (0, import_resolution.writtenValueOf)(db, key, (keys) => resolveCalls(db, keys));
|
|
1525
1518
|
}
|
|
1526
|
-
function originsOf(db,
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
(0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
|
|
1530
|
-
}
|
|
1531
|
-
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
1519
|
+
function originsOf(db, nameKey2) {
|
|
1520
|
+
(0, import_resolution.askResolution)(db, [nameKey2], "wantedOrigin");
|
|
1521
|
+
return db.facts("wantedComesFrom").filter((row) => String(row[0]) === nameKey2).map((row) => ({ module: String(row[1]), name: String(row[2]) }));
|
|
1532
1522
|
}
|
|
1533
1523
|
function subjectConstructions(db, valueKeys) {
|
|
1534
1524
|
const found = /* @__PURE__ */ new Map();
|
|
@@ -1536,10 +1526,7 @@ function subjectConstructions(db, valueKeys) {
|
|
|
1536
1526
|
return found;
|
|
1537
1527
|
}
|
|
1538
1528
|
const askSubjects = (keys) => {
|
|
1539
|
-
|
|
1540
|
-
db.add("wantedSubject", [key]);
|
|
1541
|
-
}
|
|
1542
|
-
(0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
|
|
1529
|
+
(0, import_resolution.askResolution)(db, keys, "wantedSubject");
|
|
1543
1530
|
};
|
|
1544
1531
|
askSubjects(valueKeys);
|
|
1545
1532
|
const placeholders = (0, import_resolution.placeholderValues)(db);
|
|
@@ -1586,6 +1573,7 @@ function objectReturnedBy(db, callKey) {
|
|
|
1586
1573
|
}
|
|
1587
1574
|
|
|
1588
1575
|
// src/facts/values.ts
|
|
1576
|
+
var import_resolution2 = require("@suss/resolution");
|
|
1589
1577
|
function nodeId(filePath, node) {
|
|
1590
1578
|
return `${filePath}:${node.startIndex}-${node.endIndex}`;
|
|
1591
1579
|
}
|
|
@@ -1596,10 +1584,10 @@ function readKey(filePath, node, enclosing) {
|
|
|
1596
1584
|
if (node.type !== "identifier") {
|
|
1597
1585
|
return nodeId(filePath, node);
|
|
1598
1586
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
return
|
|
1587
|
+
return nameKeyIn(filePath, enclosing, node.text);
|
|
1588
|
+
}
|
|
1589
|
+
function nameKeyIn(filePath, enclosing, name) {
|
|
1590
|
+
return nameKey(filePath, scopeChainOf(filePath, enclosing), name);
|
|
1603
1591
|
}
|
|
1604
1592
|
var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
|
|
1605
1593
|
"string",
|
|
@@ -1637,9 +1625,6 @@ function parameterName2(param) {
|
|
|
1637
1625
|
}
|
|
1638
1626
|
return children(param).find((child) => child.type === "identifier") ?? null;
|
|
1639
1627
|
}
|
|
1640
|
-
function parameterList(fn) {
|
|
1641
|
-
return parameterShapes(fn).map((parameter) => parameter.name);
|
|
1642
|
-
}
|
|
1643
1628
|
function parameterShapes(fn) {
|
|
1644
1629
|
const params = field(fn, "parameters");
|
|
1645
1630
|
const declared = [];
|
|
@@ -1654,24 +1639,52 @@ function parameterShapes(fn) {
|
|
|
1654
1639
|
}
|
|
1655
1640
|
return declared;
|
|
1656
1641
|
}
|
|
1657
|
-
function
|
|
1658
|
-
|
|
1642
|
+
function boundParameterNames(fn) {
|
|
1643
|
+
const params = field(fn, "parameters");
|
|
1644
|
+
const names = [];
|
|
1645
|
+
for (const param of params === null ? [] : children(params)) {
|
|
1646
|
+
const name = parameterName2(param);
|
|
1647
|
+
if (name !== null) {
|
|
1648
|
+
names.push(name.text);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
return names;
|
|
1659
1652
|
}
|
|
1660
1653
|
function add(emitter, relation, ...tuple) {
|
|
1661
1654
|
emitter.db.add(relation, tuple);
|
|
1662
1655
|
}
|
|
1656
|
+
function nameKey(filePath, scope, name) {
|
|
1657
|
+
for (let current = scope; current !== null; current = current.parent) {
|
|
1658
|
+
if (current.globals.has(name)) {
|
|
1659
|
+
return nameId(filePath, name);
|
|
1660
|
+
}
|
|
1661
|
+
if (current.locals.has(name)) {
|
|
1662
|
+
return `${current.funcKey}#${name}`;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
return nameId(filePath, name);
|
|
1666
|
+
}
|
|
1667
|
+
function scopeChainOf(filePath, fn) {
|
|
1668
|
+
if (fn === null) {
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
const reading = scopeReadingOf(fn);
|
|
1672
|
+
return {
|
|
1673
|
+
funcKey: nodeId(filePath, fn),
|
|
1674
|
+
locals: reading.locals,
|
|
1675
|
+
globals: reading.globals,
|
|
1676
|
+
parent: scopeChainOf(filePath, enclosingFunction(fn))
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1663
1679
|
function emitCall(emitter, call) {
|
|
1664
1680
|
add(emitter, "writtenValue", nodeId(emitter.filePath, call));
|
|
1665
1681
|
const callee = field(call, "function");
|
|
1666
|
-
|
|
1682
|
+
const args = field(call, "arguments");
|
|
1683
|
+
if (callee === null || args === null) {
|
|
1667
1684
|
return;
|
|
1668
1685
|
}
|
|
1669
1686
|
const callKey = nodeId(emitter.filePath, call);
|
|
1670
1687
|
add(emitter, "call", callKey, valueKey(emitter, callee));
|
|
1671
|
-
const args = field(call, "arguments");
|
|
1672
|
-
if (args === null) {
|
|
1673
|
-
return;
|
|
1674
|
-
}
|
|
1675
1688
|
let position = 0;
|
|
1676
1689
|
for (const argument of children(args)) {
|
|
1677
1690
|
if (argument.type === "keyword_argument") {
|
|
@@ -1702,11 +1715,7 @@ function valueKey(emitter, value) {
|
|
|
1702
1715
|
if (value.type !== "identifier") {
|
|
1703
1716
|
return nodeId(emitter.filePath, value);
|
|
1704
1717
|
}
|
|
1705
|
-
|
|
1706
|
-
if (enclosing !== null && enclosing.params.has(value.text)) {
|
|
1707
|
-
return `${enclosing.funcKey}#${value.text}`;
|
|
1708
|
-
}
|
|
1709
|
-
return nameId(emitter.filePath, value.text);
|
|
1718
|
+
return nameKey(emitter.filePath, emitter.enclosing, value.text);
|
|
1710
1719
|
}
|
|
1711
1720
|
function emitSequence(emitter, sequence) {
|
|
1712
1721
|
const objectKey = nodeId(emitter.filePath, sequence);
|
|
@@ -1796,7 +1805,6 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1796
1805
|
const funcKey = nodeId(emitter.filePath, fn);
|
|
1797
1806
|
add(emitter, "func", funcKey);
|
|
1798
1807
|
const params = field(fn, "parameters");
|
|
1799
|
-
const declared = parameterNames(fn);
|
|
1800
1808
|
let position = classKey === void 0 ? 0 : -1;
|
|
1801
1809
|
let byPosition = true;
|
|
1802
1810
|
let receiver = null;
|
|
@@ -1819,24 +1827,22 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1819
1827
|
}
|
|
1820
1828
|
position += 1;
|
|
1821
1829
|
}
|
|
1830
|
+
const reading = scopeReadingOf(fn);
|
|
1822
1831
|
const inside = {
|
|
1823
1832
|
...emitter,
|
|
1824
|
-
enclosing: {
|
|
1833
|
+
enclosing: {
|
|
1834
|
+
funcKey,
|
|
1835
|
+
locals: reading.locals,
|
|
1836
|
+
globals: reading.globals,
|
|
1837
|
+
parent: emitter.enclosing
|
|
1838
|
+
}
|
|
1825
1839
|
};
|
|
1840
|
+
emitScopeWrites(inside, reading, false);
|
|
1826
1841
|
const body = field(fn, "body");
|
|
1827
1842
|
if (body === null) {
|
|
1828
1843
|
return funcKey;
|
|
1829
1844
|
}
|
|
1830
|
-
|
|
1831
|
-
for (const child of children(node)) {
|
|
1832
|
-
if (isFunction(child)) {
|
|
1833
|
-
add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
|
|
1834
|
-
continue;
|
|
1835
|
-
}
|
|
1836
|
-
recordNested(child);
|
|
1837
|
-
}
|
|
1838
|
-
};
|
|
1839
|
-
recordNested(body);
|
|
1845
|
+
emitNestedDefinitions(inside, body);
|
|
1840
1846
|
walkExpressions(inside, body, (child) => {
|
|
1841
1847
|
if (child.type === "return_statement") {
|
|
1842
1848
|
const returned = child.namedChildren[0];
|
|
@@ -1847,29 +1853,17 @@ function emitFunctionFacts(emitter, fn, classKey) {
|
|
|
1847
1853
|
if (child.type === "call") {
|
|
1848
1854
|
add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
|
|
1849
1855
|
}
|
|
1850
|
-
if (child.type === "assignment") {
|
|
1851
|
-
|
|
1856
|
+
if (child.type === "assignment" && receiver !== null) {
|
|
1857
|
+
emitReceiverProperty(inside, child, receiver);
|
|
1852
1858
|
}
|
|
1853
1859
|
emitExpressionFact(inside, child);
|
|
1854
1860
|
});
|
|
1855
1861
|
return funcKey;
|
|
1856
1862
|
}
|
|
1857
|
-
function
|
|
1863
|
+
function emitReceiverProperty(emitter, assignment2, receiver) {
|
|
1858
1864
|
const left = field(assignment2, "left");
|
|
1859
1865
|
const right = field(assignment2, "right");
|
|
1860
|
-
if (left === null || right === null) {
|
|
1861
|
-
return;
|
|
1862
|
-
}
|
|
1863
|
-
if (left.type === "identifier") {
|
|
1864
|
-
add(
|
|
1865
|
-
emitter,
|
|
1866
|
-
"binds",
|
|
1867
|
-
nameId(emitter.filePath, left.text),
|
|
1868
|
-
valueKey(emitter, assignedValue(right))
|
|
1869
|
-
);
|
|
1870
|
-
return;
|
|
1871
|
-
}
|
|
1872
|
-
if (receiver === null || left.type !== "attribute") {
|
|
1866
|
+
if (left === null || right === null || left.type !== "attribute") {
|
|
1873
1867
|
return;
|
|
1874
1868
|
}
|
|
1875
1869
|
const object = field(left, "object");
|
|
@@ -1885,21 +1879,6 @@ function emitBinding(emitter, assignment2, receiver) {
|
|
|
1885
1879
|
valueKey(emitter, right)
|
|
1886
1880
|
);
|
|
1887
1881
|
}
|
|
1888
|
-
function emitAssignment(emitter, assignment2) {
|
|
1889
|
-
emitBinding(emitter, assignment2, null);
|
|
1890
|
-
const left = field(assignment2, "left");
|
|
1891
|
-
const right = field(assignment2, "right");
|
|
1892
|
-
if (left === null || right === null || left.type !== "identifier") {
|
|
1893
|
-
return;
|
|
1894
|
-
}
|
|
1895
|
-
add(
|
|
1896
|
-
emitter,
|
|
1897
|
-
"exportsAs",
|
|
1898
|
-
emitter.filePath,
|
|
1899
|
-
left.text,
|
|
1900
|
-
valueKey(emitter, assignedValue(right))
|
|
1901
|
-
);
|
|
1902
|
-
}
|
|
1903
1882
|
function assignedValue(right) {
|
|
1904
1883
|
if (right.type !== "subscript") {
|
|
1905
1884
|
return right;
|
|
@@ -1926,12 +1905,19 @@ function declaredMemberKey(emitter, member, classKey) {
|
|
|
1926
1905
|
}
|
|
1927
1906
|
return null;
|
|
1928
1907
|
}
|
|
1908
|
+
function writtenBaseName(base) {
|
|
1909
|
+
return base.type === "identifier" || base.type === "attribute" ? base.text : null;
|
|
1910
|
+
}
|
|
1929
1911
|
function emitClassFacts(emitter, cls) {
|
|
1930
1912
|
const classKey = nodeId(emitter.filePath, cls);
|
|
1931
1913
|
add(emitter, "objectValue", classKey);
|
|
1932
1914
|
const bases = field(cls, "superclasses");
|
|
1933
1915
|
for (const base of bases === null ? [] : children(bases)) {
|
|
1934
1916
|
add(emitter, "extends", classKey, valueKey(emitter, base));
|
|
1917
|
+
const written = writtenBaseName(base);
|
|
1918
|
+
if (written !== null) {
|
|
1919
|
+
add(emitter, "extendsNamed", classKey, written);
|
|
1920
|
+
}
|
|
1935
1921
|
}
|
|
1936
1922
|
const body = field(cls, "body");
|
|
1937
1923
|
for (const statement of body === null ? [] : children(body)) {
|
|
@@ -1950,40 +1936,361 @@ function emitClassFacts(emitter, cls) {
|
|
|
1950
1936
|
}
|
|
1951
1937
|
continue;
|
|
1952
1938
|
}
|
|
1953
|
-
const
|
|
1939
|
+
const memberKey2 = declaredMemberKey(emitter, member, classKey);
|
|
1954
1940
|
const name = field(member, "name");
|
|
1955
|
-
if (
|
|
1956
|
-
add(emitter, "holdsProperty", classKey, name.text,
|
|
1941
|
+
if (memberKey2 !== null && name !== null) {
|
|
1942
|
+
add(emitter, "holdsProperty", classKey, name.text, memberKey2);
|
|
1957
1943
|
}
|
|
1958
1944
|
}
|
|
1959
1945
|
return classKey;
|
|
1960
1946
|
}
|
|
1961
|
-
function
|
|
1962
|
-
const
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1947
|
+
function emitNestedDefinitions(emitter, node) {
|
|
1948
|
+
for (const child of children(node)) {
|
|
1949
|
+
if (isFunction(child)) {
|
|
1950
|
+
emitFunctionFacts(emitter, child);
|
|
1951
|
+
if (emitter.enclosing !== null) {
|
|
1952
|
+
add(
|
|
1953
|
+
emitter,
|
|
1954
|
+
"containsFn",
|
|
1955
|
+
emitter.enclosing.funcKey,
|
|
1956
|
+
nodeId(emitter.filePath, child)
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
continue;
|
|
1960
|
+
}
|
|
1961
|
+
if (child.type === "class_definition") {
|
|
1962
|
+
emitClassFacts(emitter, child);
|
|
1963
|
+
continue;
|
|
1964
|
+
}
|
|
1965
|
+
emitNestedDefinitions(emitter, child);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
var readingsByTree = /* @__PURE__ */ new WeakMap();
|
|
1969
|
+
function scopeReadingOf(fn) {
|
|
1970
|
+
const tree = fn.tree;
|
|
1971
|
+
let byNode = readingsByTree.get(tree);
|
|
1972
|
+
if (byNode === void 0) {
|
|
1973
|
+
byNode = /* @__PURE__ */ new Map();
|
|
1974
|
+
readingsByTree.set(tree, byNode);
|
|
1975
|
+
}
|
|
1976
|
+
const remembered = byNode.get(fn.id);
|
|
1977
|
+
if (remembered !== void 0) {
|
|
1978
|
+
return remembered;
|
|
1979
|
+
}
|
|
1980
|
+
const body = field(fn, "body");
|
|
1981
|
+
const reading = readScope(
|
|
1982
|
+
body !== null && body.type === "block" ? body : null,
|
|
1983
|
+
boundParameterNames(fn)
|
|
1984
|
+
);
|
|
1985
|
+
byNode.set(fn.id, reading);
|
|
1986
|
+
return reading;
|
|
1987
|
+
}
|
|
1988
|
+
var TARGET_PATTERN_TYPES = /* @__PURE__ */ new Set([
|
|
1989
|
+
"pattern_list",
|
|
1990
|
+
"tuple_pattern",
|
|
1991
|
+
"list_pattern"
|
|
1992
|
+
]);
|
|
1993
|
+
function targetNames(target, found = []) {
|
|
1994
|
+
if (target.type === "identifier") {
|
|
1995
|
+
found.push(target);
|
|
1996
|
+
return found;
|
|
1997
|
+
}
|
|
1998
|
+
if (TARGET_PATTERN_TYPES.has(target.type)) {
|
|
1999
|
+
for (const child of children(target)) {
|
|
2000
|
+
targetNames(child, found);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return found;
|
|
2004
|
+
}
|
|
2005
|
+
function readAssignment(node, sink) {
|
|
2006
|
+
const left = field(node, "left");
|
|
2007
|
+
const right = field(node, "right");
|
|
2008
|
+
if (left === null) {
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
const written = right === null ? null : assignedValue(right);
|
|
2012
|
+
const names = targetNames(left);
|
|
2013
|
+
for (const name of names) {
|
|
2014
|
+
sink.record(name.text, {
|
|
2015
|
+
// An unpacked target takes one piece of the value, and which piece
|
|
2016
|
+
// is not something the assignment says.
|
|
2017
|
+
value: names.length === 1 ? written : null,
|
|
2018
|
+
given: null,
|
|
2019
|
+
at: node,
|
|
2020
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
function readAugmentedAssignment(node, sink) {
|
|
2025
|
+
const left = field(node, "left");
|
|
2026
|
+
if (left === null || left.type !== "identifier") {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
sink.record(left.text, {
|
|
2030
|
+
value: null,
|
|
2031
|
+
given: null,
|
|
2032
|
+
at: node,
|
|
2033
|
+
direct: isDirectStatement(node, sink.bodyOwner)
|
|
2034
|
+
});
|
|
2035
|
+
}
|
|
2036
|
+
function readIterationTarget(node, sink) {
|
|
2037
|
+
const left = field(node, "left");
|
|
2038
|
+
if (left === null) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
for (const name of targetNames(left)) {
|
|
2042
|
+
sink.record(name.text, {
|
|
2043
|
+
value: null,
|
|
2044
|
+
given: null,
|
|
2045
|
+
at: node,
|
|
2046
|
+
direct: false
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function identifierOf(node) {
|
|
2051
|
+
if (node.type === "identifier") {
|
|
2052
|
+
return node;
|
|
2053
|
+
}
|
|
2054
|
+
return children(node).find((child) => child.type === "identifier") ?? null;
|
|
2055
|
+
}
|
|
2056
|
+
function readAsPattern(node, sink) {
|
|
2057
|
+
const target = field(node, "alias") ?? children(node)[1];
|
|
2058
|
+
const name = target === void 0 ? null : identifierOf(target);
|
|
2059
|
+
if (name === null) {
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
sink.record(name.text, {
|
|
2063
|
+
value: null,
|
|
2064
|
+
given: null,
|
|
2065
|
+
at: node,
|
|
2066
|
+
direct: false
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
function readWalrus(node, sink) {
|
|
2070
|
+
const name = field(node, "name");
|
|
2071
|
+
if (name === null || name.type !== "identifier") {
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
sink.record(name.text, {
|
|
2075
|
+
value: field(node, "value"),
|
|
2076
|
+
given: null,
|
|
2077
|
+
at: node,
|
|
2078
|
+
direct: false
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
function readGlobalStatement(node, sink) {
|
|
2082
|
+
for (const child of children(node)) {
|
|
2083
|
+
if (child.type === "identifier") {
|
|
2084
|
+
sink.globals.add(child.text);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
function readNonlocalStatement(node, sink) {
|
|
2089
|
+
for (const child of children(node)) {
|
|
2090
|
+
if (child.type === "identifier") {
|
|
2091
|
+
sink.nonlocals.add(child.text);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
var WRITE_READERS = {
|
|
2096
|
+
assignment: readAssignment,
|
|
2097
|
+
augmented_assignment: readAugmentedAssignment,
|
|
2098
|
+
for_statement: readIterationTarget,
|
|
2099
|
+
for_in_clause: readIterationTarget,
|
|
2100
|
+
as_pattern: readAsPattern,
|
|
2101
|
+
named_expression: readWalrus,
|
|
2102
|
+
global_statement: readGlobalStatement,
|
|
2103
|
+
nonlocal_statement: readNonlocalStatement
|
|
2104
|
+
};
|
|
2105
|
+
function readScope(bodyOwner, parameterNames) {
|
|
2106
|
+
const writes = /* @__PURE__ */ new Map();
|
|
2107
|
+
const globals = /* @__PURE__ */ new Set();
|
|
2108
|
+
const nonlocals = /* @__PURE__ */ new Set();
|
|
2109
|
+
const record = (name, write) => {
|
|
2110
|
+
const existing = writes.get(name);
|
|
2111
|
+
if (existing === void 0) {
|
|
2112
|
+
writes.set(name, [write]);
|
|
1966
2113
|
return;
|
|
1967
2114
|
}
|
|
1968
|
-
|
|
1969
|
-
add(emitter, "exportsAs", filePath, name.text, key);
|
|
2115
|
+
existing.push(write);
|
|
1970
2116
|
};
|
|
1971
|
-
const
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2117
|
+
const sink = { bodyOwner, globals, nonlocals, record };
|
|
2118
|
+
const visit = (node) => {
|
|
2119
|
+
if (isFunction(node) || node.type === "class_definition") {
|
|
2120
|
+
const name = field(node, "name");
|
|
2121
|
+
if (name !== null) {
|
|
2122
|
+
record(name.text, {
|
|
2123
|
+
value: node,
|
|
2124
|
+
given: null,
|
|
2125
|
+
at: node,
|
|
2126
|
+
direct: isDirectStatement(node, bodyOwner)
|
|
2127
|
+
});
|
|
1982
2128
|
}
|
|
1983
|
-
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
WRITE_READERS[node.type]?.(node, sink);
|
|
2132
|
+
for (const child of children(node)) {
|
|
2133
|
+
visit(child);
|
|
1984
2134
|
}
|
|
1985
2135
|
};
|
|
1986
|
-
|
|
2136
|
+
for (const statement of bodyOwner === null ? [] : children(bodyOwner)) {
|
|
2137
|
+
visit(statement);
|
|
2138
|
+
}
|
|
2139
|
+
const locals = new Set(writes.keys());
|
|
2140
|
+
for (const name of [...globals, ...nonlocals]) {
|
|
2141
|
+
locals.delete(name);
|
|
2142
|
+
}
|
|
2143
|
+
for (const name of parameterNames) {
|
|
2144
|
+
locals.add(name);
|
|
2145
|
+
addParameterWrite(writes.get(name), name);
|
|
2146
|
+
}
|
|
2147
|
+
return { bodyOwner, writes, locals, globals };
|
|
2148
|
+
}
|
|
2149
|
+
function addParameterWrite(later, name) {
|
|
2150
|
+
const first = later?.[0];
|
|
2151
|
+
if (later === void 0 || first === void 0) {
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
later.unshift({ value: null, given: name, at: first.at, direct: false });
|
|
2155
|
+
}
|
|
2156
|
+
function statementOf(node) {
|
|
2157
|
+
let current = node;
|
|
2158
|
+
while (current !== null) {
|
|
2159
|
+
const parent = current.parent;
|
|
2160
|
+
if (parent === null) {
|
|
2161
|
+
return null;
|
|
2162
|
+
}
|
|
2163
|
+
if (parent.type === "block" || parent.type === "module") {
|
|
2164
|
+
return current;
|
|
2165
|
+
}
|
|
2166
|
+
current = parent;
|
|
2167
|
+
}
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
function isDirectStatement(node, bodyOwner) {
|
|
2171
|
+
if (bodyOwner === null) {
|
|
2172
|
+
return false;
|
|
2173
|
+
}
|
|
2174
|
+
const statement = statementOf(node);
|
|
2175
|
+
return statement !== null && statement.parent?.id === bodyOwner.id;
|
|
2176
|
+
}
|
|
2177
|
+
var UNREAD_PARENTS = /* @__PURE__ */ new Set([
|
|
2178
|
+
"as_pattern_target",
|
|
2179
|
+
"global_statement",
|
|
2180
|
+
"nonlocal_statement",
|
|
2181
|
+
"pattern_list",
|
|
2182
|
+
"tuple_pattern",
|
|
2183
|
+
"list_pattern",
|
|
2184
|
+
"dotted_name"
|
|
2185
|
+
]);
|
|
2186
|
+
var UNREAD_FIELDS = {
|
|
2187
|
+
assignment: "left",
|
|
2188
|
+
augmented_assignment: "left",
|
|
2189
|
+
for_statement: "left",
|
|
2190
|
+
for_in_clause: "left",
|
|
2191
|
+
named_expression: "name",
|
|
2192
|
+
function_definition: "name",
|
|
2193
|
+
class_definition: "name",
|
|
2194
|
+
attribute: "attribute",
|
|
2195
|
+
keyword_argument: "name"
|
|
2196
|
+
};
|
|
2197
|
+
function isNameRead(name) {
|
|
2198
|
+
const parent = name.parent;
|
|
2199
|
+
if (parent === null) {
|
|
2200
|
+
return true;
|
|
2201
|
+
}
|
|
2202
|
+
if (UNREAD_PARENTS.has(parent.type)) {
|
|
2203
|
+
return false;
|
|
2204
|
+
}
|
|
2205
|
+
const fieldName = UNREAD_FIELDS[parent.type];
|
|
2206
|
+
return fieldName === void 0 || field(parent, fieldName)?.id !== name.id;
|
|
2207
|
+
}
|
|
2208
|
+
var READ_FIRST = {
|
|
2209
|
+
call: "function",
|
|
2210
|
+
attribute: "object"
|
|
2211
|
+
};
|
|
2212
|
+
function readFirst(node) {
|
|
2213
|
+
if (node.type === "parenthesized_expression") {
|
|
2214
|
+
return children(node)[0] ?? null;
|
|
2215
|
+
}
|
|
2216
|
+
const fieldName = READ_FIRST[node.type];
|
|
2217
|
+
return fieldName === void 0 ? null : field(node, fieldName);
|
|
2218
|
+
}
|
|
2219
|
+
var NAME_READS = {
|
|
2220
|
+
nameTypes: /* @__PURE__ */ new Set(["identifier"]),
|
|
2221
|
+
laterBodies: LATER_BODY_TYPES,
|
|
2222
|
+
childrenOf: children,
|
|
2223
|
+
isRead: isNameRead,
|
|
2224
|
+
readFirst
|
|
2225
|
+
};
|
|
2226
|
+
var CONSTRUCTION_TYPES = /* @__PURE__ */ new Set([
|
|
2227
|
+
"call",
|
|
2228
|
+
"list",
|
|
2229
|
+
"dictionary",
|
|
2230
|
+
"set",
|
|
2231
|
+
"tuple",
|
|
2232
|
+
"list_comprehension",
|
|
2233
|
+
"dictionary_comprehension",
|
|
2234
|
+
"set_comprehension",
|
|
2235
|
+
"generator_expression"
|
|
2236
|
+
]);
|
|
2237
|
+
function describeWrite(emitter, write, name) {
|
|
2238
|
+
if (write.value === null) {
|
|
2239
|
+
return {
|
|
2240
|
+
value: write.given,
|
|
2241
|
+
placeholder: false,
|
|
2242
|
+
construction: null,
|
|
2243
|
+
narrowsName: false
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
value: valueKey(emitter, write.value),
|
|
2248
|
+
placeholder: write.value.type === "none",
|
|
2249
|
+
construction: CONSTRUCTION_TYPES.has(write.value.type) ? write.value.text.replace(/\s+/g, " ").trim() : null,
|
|
2250
|
+
narrowsName: write.value.type === "call" && (0, import_resolution2.startsAtName)(write.value, name, NAME_READS)
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
function settledValue(emitter, reading, name, writes) {
|
|
2254
|
+
const only = writes.length === 1 ? writes[0] : void 0;
|
|
2255
|
+
if (only !== void 0) {
|
|
2256
|
+
return only.value === null ? only.given : valueKey(emitter, only.value);
|
|
2257
|
+
}
|
|
2258
|
+
return (0, import_resolution2.valueLeftByWrites)(
|
|
2259
|
+
writes.map((write) => describeWrite(emitter, write, name)),
|
|
2260
|
+
(0, import_resolution2.writesRunInOrder)(reading.bodyOwner, name, writes, NAME_READS)
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
function emitScopeWrites(emitter, reading, atModule) {
|
|
2264
|
+
for (const [name, writes] of reading.writes) {
|
|
2265
|
+
const settled = settledValue(emitter, reading, name, writes);
|
|
2266
|
+
const key = nameKey(emitter.filePath, emitter.enclosing, name);
|
|
2267
|
+
if (settled === null) {
|
|
2268
|
+
emitCandidates(emitter, key, name, writes);
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
add(emitter, writes.length === 1 ? "binds" : "endsHolding", key, settled);
|
|
2272
|
+
if (atModule) {
|
|
2273
|
+
add(emitter, "exportsAs", emitter.filePath, name, settled);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
function emitCandidates(emitter, key, name, writes) {
|
|
2278
|
+
for (const write of writes) {
|
|
2279
|
+
if (write.value === null) {
|
|
2280
|
+
if (write.given === null) {
|
|
2281
|
+
add(emitter, "writesUnstated", key);
|
|
2282
|
+
}
|
|
2283
|
+
continue;
|
|
2284
|
+
}
|
|
2285
|
+
if (!describeWrite(emitter, write, name).narrowsName) {
|
|
2286
|
+
add(emitter, "mayHold", key, valueKey(emitter, write.value));
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
function emitValueFacts(db, filePath, root) {
|
|
2291
|
+
const emitter = { db, filePath, enclosing: null };
|
|
2292
|
+
emitNestedDefinitions(emitter, root);
|
|
2293
|
+
emitScopeWrites(emitter, readScope(root, []), true);
|
|
1987
2294
|
emitExpressionFacts(emitter, root);
|
|
1988
2295
|
}
|
|
1989
2296
|
|
|
@@ -2069,7 +2376,7 @@ function resolveModule(importingFile, spec, options) {
|
|
|
2069
2376
|
}
|
|
2070
2377
|
|
|
2071
2378
|
// src/values/evaluator.ts
|
|
2072
|
-
var
|
|
2379
|
+
var import_resolution3 = require("@suss/resolution");
|
|
2073
2380
|
var import_values3 = require("@suss/values");
|
|
2074
2381
|
|
|
2075
2382
|
// src/values/lowering.ts
|
|
@@ -2096,7 +2403,7 @@ function pythonLowering(options) {
|
|
|
2096
2403
|
return {
|
|
2097
2404
|
idOf: (node) => node.id,
|
|
2098
2405
|
expression: (node) => expressionOf(node, originOf2),
|
|
2099
|
-
statement:
|
|
2406
|
+
statement: statementOf2,
|
|
2100
2407
|
siteOf,
|
|
2101
2408
|
functionOf,
|
|
2102
2409
|
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
@@ -2360,7 +2667,7 @@ var STATEMENT_TYPES = {
|
|
|
2360
2667
|
body: blockStatements2(field(node, "body"))
|
|
2361
2668
|
})
|
|
2362
2669
|
};
|
|
2363
|
-
function
|
|
2670
|
+
function statementOf2(node) {
|
|
2364
2671
|
const lower = STATEMENT_TYPES[node.type];
|
|
2365
2672
|
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2366
2673
|
}
|
|
@@ -2857,7 +3164,7 @@ function contextOver(db, nodes) {
|
|
|
2857
3164
|
return null;
|
|
2858
3165
|
}
|
|
2859
3166
|
const answer = writtenValueOf(db, key);
|
|
2860
|
-
return answer === null ? null : (0,
|
|
3167
|
+
return answer === null ? null : (0, import_resolution3.nodeOfKey)(rootsByFile, answer);
|
|
2861
3168
|
},
|
|
2862
3169
|
callable: (call) => {
|
|
2863
3170
|
const callee = field(call, "function");
|
|
@@ -2986,7 +3293,7 @@ function composedPrefixOf(index, construction, routerPath) {
|
|
|
2986
3293
|
return { kind: "composed", value: composed.value + ownPrefix.value };
|
|
2987
3294
|
}
|
|
2988
3295
|
function composedMountPrefix(index, construction) {
|
|
2989
|
-
const paths = (0,
|
|
3296
|
+
const paths = (0, import_resolution4.mountPathsOf)(mountEdgesOf(index), construction.valueKey);
|
|
2990
3297
|
if (paths === null) {
|
|
2991
3298
|
return {
|
|
2992
3299
|
kind: "abstain",
|
|
@@ -3409,9 +3716,13 @@ function constructionThroughFacts(name, scope, scan) {
|
|
|
3409
3716
|
if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
|
|
3410
3717
|
return null;
|
|
3411
3718
|
}
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
|
|
3719
|
+
const nameKey2 = nameKeyIn(
|
|
3720
|
+
scan.bound.file,
|
|
3721
|
+
scope.kind === "function" ? scope.node : enclosingFunction(scope.node),
|
|
3722
|
+
name
|
|
3723
|
+
);
|
|
3724
|
+
const constructionKey2 = subjectConstructions(facts, [nameKey2]).get(
|
|
3725
|
+
nameKey2
|
|
3415
3726
|
)?.constructionKey;
|
|
3416
3727
|
return constructionKey2 === void 0 ? null : scan.index.byValueKey.get(constructionKey2) ?? null;
|
|
3417
3728
|
}
|
|
@@ -3502,11 +3813,11 @@ function walkStatements(statements, position, scan, visit) {
|
|
|
3502
3813
|
visit(stmt, position, scan);
|
|
3503
3814
|
}
|
|
3504
3815
|
}
|
|
3505
|
-
function
|
|
3816
|
+
function targetNames2(node) {
|
|
3506
3817
|
if (node.type === "identifier") {
|
|
3507
3818
|
return [node.text];
|
|
3508
3819
|
}
|
|
3509
|
-
return bodyStatements(node).flatMap(
|
|
3820
|
+
return bodyStatements(node).flatMap(targetNames2);
|
|
3510
3821
|
}
|
|
3511
3822
|
function sequenceElementNames(node) {
|
|
3512
3823
|
if (node === null || node.type !== "list" && node.type !== "tuple") {
|
|
@@ -3543,7 +3854,7 @@ function loopBindingsOf(stmt, position, scan) {
|
|
|
3543
3854
|
});
|
|
3544
3855
|
return bindings;
|
|
3545
3856
|
}
|
|
3546
|
-
for (const name of
|
|
3857
|
+
for (const name of targetNames2(left)) {
|
|
3547
3858
|
bindings.set(name, { kind: "unenumerable", location });
|
|
3548
3859
|
}
|
|
3549
3860
|
return bindings;
|
|
@@ -5019,14 +5330,9 @@ function matchedChains(calls, options) {
|
|
|
5019
5330
|
if (chains.length === 0) {
|
|
5020
5331
|
return [];
|
|
5021
5332
|
}
|
|
5022
|
-
const alreadyAsked = new Set(
|
|
5023
|
-
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
5024
|
-
);
|
|
5025
5333
|
resolveCalls(
|
|
5026
5334
|
options.facts,
|
|
5027
|
-
storageCallees(calls, options.filePath, options.couldMatch)
|
|
5028
|
-
(key) => !alreadyAsked.has(key)
|
|
5029
|
-
)
|
|
5335
|
+
storageCallees(calls, options.filePath, options.couldMatch)
|
|
5030
5336
|
);
|
|
5031
5337
|
const matched = [];
|
|
5032
5338
|
for (const chain of chains) {
|
|
@@ -6087,8 +6393,8 @@ function isInjectedParameter(param, injectedCallees, scope, ctx) {
|
|
|
6087
6393
|
// src/facts/why.ts
|
|
6088
6394
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
6089
6395
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
6090
|
-
var
|
|
6091
|
-
var
|
|
6396
|
+
var import_datalog3 = require("@suss/datalog");
|
|
6397
|
+
var import_resolution6 = require("@suss/resolution");
|
|
6092
6398
|
|
|
6093
6399
|
// src/facts.ts
|
|
6094
6400
|
function unitKey(filePath, range, name) {
|
|
@@ -6106,7 +6412,7 @@ function emitModuleImportFacts(db, filePath, module2, resolverOptions) {
|
|
|
6106
6412
|
if (binding.kind !== "import" && binding.kind !== "importFrom") {
|
|
6107
6413
|
continue;
|
|
6108
6414
|
}
|
|
6109
|
-
const
|
|
6415
|
+
const nameKey2 = `${filePath}#${localName}`;
|
|
6110
6416
|
const moduleText = importedModuleText(
|
|
6111
6417
|
binding.module,
|
|
6112
6418
|
binding.relativeLevel
|
|
@@ -6122,16 +6428,16 @@ function emitModuleImportFacts(db, filePath, module2, resolverOptions) {
|
|
|
6122
6428
|
resolution.status === "resolved" ? "resolved" : resolution.reason
|
|
6123
6429
|
]);
|
|
6124
6430
|
const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
|
|
6125
|
-
db.add("pyImportedName", [
|
|
6431
|
+
db.add("pyImportedName", [nameKey2, moduleText, importedName]);
|
|
6126
6432
|
if (scope.kind === "module") {
|
|
6127
|
-
db.add("exportsAs", [filePath, localName,
|
|
6433
|
+
db.add("exportsAs", [filePath, localName, nameKey2]);
|
|
6128
6434
|
}
|
|
6129
6435
|
if (resolution.status === "resolved") {
|
|
6130
6436
|
db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
|
|
6131
|
-
db.add("imports", [
|
|
6437
|
+
db.add("imports", [nameKey2, resolution.file, importedName]);
|
|
6132
6438
|
continue;
|
|
6133
6439
|
}
|
|
6134
|
-
db.add("imports", [
|
|
6440
|
+
db.add("imports", [nameKey2, moduleText, importedName]);
|
|
6135
6441
|
}
|
|
6136
6442
|
}
|
|
6137
6443
|
for (const openModule of module2.openImports) {
|
|
@@ -6199,7 +6505,7 @@ function parseWith(language, source) {
|
|
|
6199
6505
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
6200
6506
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
6201
6507
|
var import_behavioral_ir5 = require("@suss/behavioral-ir");
|
|
6202
|
-
var
|
|
6508
|
+
var import_datalog2 = require("@suss/datalog");
|
|
6203
6509
|
var import_extractor10 = require("@suss/extractor");
|
|
6204
6510
|
|
|
6205
6511
|
// src/diagnostics.ts
|
|
@@ -6271,272 +6577,246 @@ function definedNodeOf(binding) {
|
|
|
6271
6577
|
|
|
6272
6578
|
// src/reach/closure.ts
|
|
6273
6579
|
var import_behavioral_ir4 = require("@suss/behavioral-ir");
|
|
6274
|
-
var
|
|
6580
|
+
var import_datalog = require("@suss/datalog");
|
|
6275
6581
|
var import_extractor7 = require("@suss/extractor");
|
|
6276
6582
|
|
|
6277
6583
|
// src/reach/resolveCallee.ts
|
|
6278
|
-
var
|
|
6279
|
-
var
|
|
6280
|
-
|
|
6281
|
-
|
|
6584
|
+
var import_resolution5 = require("@suss/resolution");
|
|
6585
|
+
var STOP_FOR = {
|
|
6586
|
+
severalSources: "multipleSources",
|
|
6587
|
+
outsideRun: "outsideRun",
|
|
6588
|
+
unsettled: "unsettledValue",
|
|
6589
|
+
undeclared: "noDeclaration"
|
|
6282
6590
|
};
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6591
|
+
var NO_DECLARATION = {
|
|
6592
|
+
kind: "stopped",
|
|
6593
|
+
reason: "noDeclaration"
|
|
6594
|
+
};
|
|
6595
|
+
function calleeSpellings(calls, ctx) {
|
|
6596
|
+
const spellingOf = /* @__PURE__ */ new Map();
|
|
6597
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6598
|
+
for (const { call, site } of calls) {
|
|
6599
|
+
const spelling = spellingFor(call, site, ctx);
|
|
6600
|
+
spellingOf.set(call.id, spelling);
|
|
6601
|
+
if (spelling.kind === "key") {
|
|
6602
|
+
keys.add(spelling.key);
|
|
6603
|
+
}
|
|
6287
6604
|
}
|
|
6288
|
-
return
|
|
6605
|
+
return { spellingOf, outcomes: (0, import_resolution5.calleeOutcomes)(ctx.facts, [...keys]) };
|
|
6289
6606
|
}
|
|
6290
|
-
function
|
|
6291
|
-
|
|
6292
|
-
|
|
6607
|
+
function resolveCallee2(call, site, ctx, read) {
|
|
6608
|
+
const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site, ctx);
|
|
6609
|
+
if (spelling.kind === "stopped") {
|
|
6610
|
+
return spelling;
|
|
6293
6611
|
}
|
|
6294
|
-
const
|
|
6295
|
-
|
|
6612
|
+
const outcome = read?.outcomes.get(spelling.key) ?? (0, import_resolution5.calleeOutcomeOf)(ctx.facts, spelling.key);
|
|
6613
|
+
return asCallee(outcome, site.owner, ctx);
|
|
6614
|
+
}
|
|
6615
|
+
function functionNamed(nameKey2, ctx) {
|
|
6616
|
+
if (assignedElsewhere(ctx.facts, nameKey2)) {
|
|
6296
6617
|
return null;
|
|
6297
6618
|
}
|
|
6298
|
-
const
|
|
6299
|
-
return
|
|
6619
|
+
const outcome = (0, import_resolution5.calleeOutcomeOf)(ctx.facts, nameKey2);
|
|
6620
|
+
return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
|
|
6300
6621
|
}
|
|
6301
|
-
function
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
if (resolved.kind === "class") {
|
|
6306
|
-
return asCallee(
|
|
6307
|
-
memberOfClass(resolved.file, resolved.node, "__init__", ctx),
|
|
6308
|
-
ctx
|
|
6309
|
-
);
|
|
6310
|
-
}
|
|
6311
|
-
if (resolved.kind === "stop") {
|
|
6312
|
-
return { kind: "stopped", reason: resolved.reason };
|
|
6313
|
-
}
|
|
6314
|
-
if (resolved.kind === "package") {
|
|
6315
|
-
return { kind: "stopped", reason: "outsideRun" };
|
|
6316
|
-
}
|
|
6317
|
-
return { kind: "stopped", reason: "noDeclaration" };
|
|
6622
|
+
function assignedElsewhere(facts, nameKey2) {
|
|
6623
|
+
return (0, import_resolution5.writtenSourcesOf)(facts, nameKey2).some(
|
|
6624
|
+
(source) => !facts.has("func", [source])
|
|
6625
|
+
);
|
|
6318
6626
|
}
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
const object = field(node, "object");
|
|
6323
|
-
const attribute = field(node, "attribute");
|
|
6324
|
-
if (object === null || attribute === null) {
|
|
6325
|
-
return stop("noDeclaration");
|
|
6326
|
-
}
|
|
6327
|
-
return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
|
|
6328
|
-
},
|
|
6329
|
-
call: (node, site, ctx) => {
|
|
6330
|
-
const callee = field(node, "function");
|
|
6331
|
-
return callee === null ? stop("noDeclaration") : instanceBuiltBy(resolveExpression(callee, site, ctx), "noDeclaration");
|
|
6332
|
-
},
|
|
6333
|
-
parenthesized_expression: (node, site, ctx) => {
|
|
6334
|
-
const inner = node.namedChildren[0];
|
|
6335
|
-
return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
|
|
6627
|
+
function readThrough(node) {
|
|
6628
|
+
if (node?.type !== "parenthesized_expression") {
|
|
6629
|
+
return node;
|
|
6336
6630
|
}
|
|
6337
|
-
|
|
6338
|
-
function resolveExpression(node, site, ctx) {
|
|
6339
|
-
const resolver = EXPRESSION_RESOLVERS[node.type];
|
|
6340
|
-
return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
|
|
6631
|
+
return readThrough(children(node)[0] ?? null);
|
|
6341
6632
|
}
|
|
6342
|
-
function
|
|
6343
|
-
|
|
6344
|
-
|
|
6633
|
+
function spellingFor(call, site, ctx) {
|
|
6634
|
+
const callee = readThrough(field(call, "function"));
|
|
6635
|
+
if (callee === null) {
|
|
6636
|
+
return { kind: "stopped", reason: "noDeclaration" };
|
|
6345
6637
|
}
|
|
6346
|
-
return
|
|
6638
|
+
return throughModules(callee, site, ctx) ?? {
|
|
6639
|
+
kind: "key",
|
|
6640
|
+
key: readKey(site.file.file, callee, enclosingFunction(callee))
|
|
6641
|
+
};
|
|
6347
6642
|
}
|
|
6348
|
-
function
|
|
6349
|
-
if (
|
|
6350
|
-
return
|
|
6643
|
+
function asCallee(outcome, owner, ctx) {
|
|
6644
|
+
if (outcome.kind === "function") {
|
|
6645
|
+
return functionCallee(outcome.key, ctx);
|
|
6351
6646
|
}
|
|
6352
|
-
if (
|
|
6353
|
-
return
|
|
6647
|
+
if (outcome.kind === "object") {
|
|
6648
|
+
return constructorOf(outcome.key, ctx);
|
|
6354
6649
|
}
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6650
|
+
if (outcome.kind === "callerSupplied") {
|
|
6651
|
+
return outcome.key.startsWith(`${owner}#`) ? { kind: "stopped", reason: "callerSupplied" } : { kind: "stopped", reason: "unsettledValue" };
|
|
6358
6652
|
}
|
|
6359
|
-
return
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6653
|
+
return {
|
|
6654
|
+
kind: "stopped",
|
|
6655
|
+
reason: STOP_FOR[outcome.kind] ?? "noDeclaration"
|
|
6656
|
+
};
|
|
6363
6657
|
}
|
|
6364
|
-
function
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
return stop("noDeclaration");
|
|
6658
|
+
function functionCallee(key, ctx) {
|
|
6659
|
+
if (ctx.definitions.get(key)?.type === "lambda") {
|
|
6660
|
+
return { kind: "stopped", reason: "unsettledValue" };
|
|
6368
6661
|
}
|
|
6369
|
-
|
|
6662
|
+
const target = functionAt(key, ctx);
|
|
6663
|
+
return target === null ? NO_DECLARATION : { kind: "followed", target };
|
|
6370
6664
|
}
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
}),
|
|
6376
|
-
classDef: (binding, file) => ({ kind: "class", file, node: binding.node }),
|
|
6377
|
-
parameter: () => stop("callerSupplied"),
|
|
6378
|
-
// `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
|
|
6379
|
-
import: (binding, file, _scope, ctx) => moduleNamed(
|
|
6380
|
-
file,
|
|
6381
|
-
binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
|
|
6382
|
-
0,
|
|
6383
|
-
ctx
|
|
6384
|
-
),
|
|
6385
|
-
importFrom: (binding, file, _scope, ctx, visited) => memberOf(
|
|
6386
|
-
moduleNamed(file, binding.module, binding.relativeLevel, ctx),
|
|
6387
|
-
binding.importedName,
|
|
6388
|
-
ctx,
|
|
6389
|
-
visited
|
|
6390
|
-
),
|
|
6391
|
-
// `run = load` is an alias worth one hop; anything else is a value.
|
|
6392
|
-
assignment: (binding, file, scope, ctx, visited) => aliasedValue(binding.value, file, scope, ctx, visited),
|
|
6393
|
-
global: () => stop("unsettledValue"),
|
|
6394
|
-
nonlocal: () => stop("unsettledValue")
|
|
6395
|
-
};
|
|
6396
|
-
function resolveBinding(binding, file, scope, ctx, visited) {
|
|
6397
|
-
const resolver = BINDING_RESOLVERS[binding.kind];
|
|
6398
|
-
return resolver(binding, file, scope, ctx, visited);
|
|
6399
|
-
}
|
|
6400
|
-
function aliasedValue(value, file, scope, ctx, visited) {
|
|
6401
|
-
if (value === null) {
|
|
6402
|
-
return stop("unsettledValue");
|
|
6403
|
-
}
|
|
6404
|
-
if (value.type === "identifier") {
|
|
6405
|
-
const binding = resolveName(scope, value.text);
|
|
6406
|
-
if (binding === null || binding.kind === "assignment") {
|
|
6407
|
-
return stop("unsettledValue");
|
|
6408
|
-
}
|
|
6409
|
-
return resolveBinding(binding, file, scope, ctx, visited);
|
|
6410
|
-
}
|
|
6411
|
-
if (value.type === "call") {
|
|
6412
|
-
const callee = field(value, "function");
|
|
6413
|
-
return callee === null ? stop("unsettledValue") : instanceBuiltBy(
|
|
6414
|
-
aliasedValue(callee, file, scope, ctx, visited),
|
|
6415
|
-
"unsettledValue"
|
|
6416
|
-
);
|
|
6665
|
+
function constructorOf(classKey, ctx) {
|
|
6666
|
+
const declared = ctx.facts.lookup("holdsProperty", 0, classKey).find((row) => String(row[1]) === "__init__");
|
|
6667
|
+
if (declared === void 0) {
|
|
6668
|
+
return NO_DECLARATION;
|
|
6417
6669
|
}
|
|
6418
|
-
|
|
6419
|
-
const object = field(value, "object");
|
|
6420
|
-
const attribute = field(value, "attribute");
|
|
6421
|
-
if (object === null || attribute === null) {
|
|
6422
|
-
return stop("unsettledValue");
|
|
6423
|
-
}
|
|
6424
|
-
return memberOf(
|
|
6425
|
-
aliasedValue(object, file, scope, ctx, visited),
|
|
6426
|
-
attribute.text,
|
|
6427
|
-
ctx
|
|
6428
|
-
);
|
|
6429
|
-
}
|
|
6430
|
-
return stop("unsettledValue");
|
|
6670
|
+
return functionCallee(String(declared[2]), ctx);
|
|
6431
6671
|
}
|
|
6432
|
-
function
|
|
6672
|
+
function functionAt(key, ctx) {
|
|
6673
|
+
const node = ctx.definitions.get(key);
|
|
6674
|
+
const file = ctx.filesByPath.get(key.slice(0, key.lastIndexOf(":")));
|
|
6675
|
+
if (node === void 0 || file === void 0) {
|
|
6676
|
+
return null;
|
|
6677
|
+
}
|
|
6433
6678
|
const name = field(node, "name")?.text ?? "<anon>";
|
|
6434
|
-
const
|
|
6679
|
+
const owner = ownerClassOf(node);
|
|
6680
|
+
const ownerName = owner === null ? void 0 : field(owner, "name")?.text;
|
|
6435
6681
|
return {
|
|
6436
6682
|
file,
|
|
6437
6683
|
node,
|
|
6438
6684
|
name,
|
|
6439
|
-
exportPath: ownerName ===
|
|
6685
|
+
exportPath: ownerName === void 0 ? [name] : [ownerName, name]
|
|
6440
6686
|
};
|
|
6441
6687
|
}
|
|
6442
|
-
function
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6688
|
+
function ownerClassOf(node) {
|
|
6689
|
+
for (let up = node.parent; up !== null; up = up.parent) {
|
|
6690
|
+
if (isFunction(up)) {
|
|
6691
|
+
return null;
|
|
6692
|
+
}
|
|
6693
|
+
if (up.type === "class_definition") {
|
|
6694
|
+
return up;
|
|
6695
|
+
}
|
|
6450
6696
|
}
|
|
6451
|
-
|
|
6452
|
-
return file === void 0 ? stop("outsideRun") : { kind: "module", file };
|
|
6697
|
+
return null;
|
|
6453
6698
|
}
|
|
6454
|
-
function
|
|
6455
|
-
if (
|
|
6456
|
-
return
|
|
6699
|
+
function throughModules(callee, site, ctx) {
|
|
6700
|
+
if (callee.type === "identifier") {
|
|
6701
|
+
return resolveName(site.scope, callee.text) === null ? openImportMember(site.file, callee.text, ctx, /* @__PURE__ */ new Set()) : null;
|
|
6457
6702
|
}
|
|
6458
|
-
if (
|
|
6459
|
-
return
|
|
6460
|
-
base.from,
|
|
6461
|
-
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
6462
|
-
base.relativeLevel,
|
|
6463
|
-
ctx
|
|
6464
|
-
);
|
|
6703
|
+
if (callee.type !== "attribute") {
|
|
6704
|
+
return null;
|
|
6465
6705
|
}
|
|
6466
|
-
|
|
6467
|
-
|
|
6706
|
+
const object = field(callee, "object");
|
|
6707
|
+
const attribute = field(callee, "attribute");
|
|
6708
|
+
if (object === null || attribute === null) {
|
|
6709
|
+
return null;
|
|
6468
6710
|
}
|
|
6469
|
-
|
|
6470
|
-
|
|
6711
|
+
const base = moduleAt(object, site, ctx);
|
|
6712
|
+
return base === null ? null : memberKey(base, attribute.text, ctx);
|
|
6713
|
+
}
|
|
6714
|
+
function openImportMember(file, name, ctx, visited) {
|
|
6715
|
+
if (visited.has(file.file)) {
|
|
6716
|
+
return null;
|
|
6471
6717
|
}
|
|
6472
|
-
|
|
6718
|
+
visited.add(file.file);
|
|
6719
|
+
const found = /* @__PURE__ */ new Set();
|
|
6720
|
+
for (const spec of file.module.openImports) {
|
|
6721
|
+
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6722
|
+
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6723
|
+
if (opened === null || opened.kind !== "module") {
|
|
6724
|
+
continue;
|
|
6725
|
+
}
|
|
6726
|
+
const member = opened.file.module.moduleScope.bindings.has(name) ? { kind: "key", key: `${opened.file.file}#${name}` } : openImportMember(opened.file, name, ctx, visited);
|
|
6727
|
+
if (member?.kind === "key") {
|
|
6728
|
+
found.add(member.key);
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
if (found.size > 1) {
|
|
6732
|
+
return { kind: "stopped", reason: "multipleSources" };
|
|
6733
|
+
}
|
|
6734
|
+
const only = [...found][0];
|
|
6735
|
+
return only === void 0 ? null : { kind: "key", key: only };
|
|
6473
6736
|
}
|
|
6474
|
-
function
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
if (binding === void 0) {
|
|
6478
|
-
return stop("noDeclaration");
|
|
6737
|
+
function moduleAt(node, site, ctx) {
|
|
6738
|
+
if (node.type === "identifier") {
|
|
6739
|
+
return importedModule(node.text, site, ctx);
|
|
6479
6740
|
}
|
|
6480
|
-
if (
|
|
6481
|
-
return
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6741
|
+
if (node.type !== "attribute") {
|
|
6742
|
+
return null;
|
|
6743
|
+
}
|
|
6744
|
+
const object = field(node, "object");
|
|
6745
|
+
const attribute = field(node, "attribute");
|
|
6746
|
+
if (object === null || attribute === null) {
|
|
6747
|
+
return null;
|
|
6485
6748
|
}
|
|
6486
|
-
|
|
6749
|
+
const base = moduleAt(object, site, ctx);
|
|
6750
|
+
return base === null ? null : submoduleOf(base, attribute.text, ctx);
|
|
6487
6751
|
}
|
|
6488
|
-
function
|
|
6489
|
-
const
|
|
6490
|
-
if (
|
|
6491
|
-
|
|
6752
|
+
function importedModule(name, site, ctx) {
|
|
6753
|
+
const binding = resolveName(site.scope, name);
|
|
6754
|
+
if (binding?.kind === "import") {
|
|
6755
|
+
const head = binding.module.split(".")[0];
|
|
6756
|
+
const dotted = binding.localName === head ? binding.localName : binding.module;
|
|
6757
|
+
return moduleNamed(site.file, dotted, 0, ctx);
|
|
6492
6758
|
}
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
if (binding !== void 0) {
|
|
6496
|
-
return resolveBinding(binding, file, file.module.moduleScope, ctx, visited);
|
|
6759
|
+
if (binding?.kind !== "importFrom") {
|
|
6760
|
+
return null;
|
|
6497
6761
|
}
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6762
|
+
const from = moduleNamed(
|
|
6763
|
+
site.file,
|
|
6764
|
+
binding.module,
|
|
6765
|
+
binding.relativeLevel,
|
|
6766
|
+
ctx
|
|
6767
|
+
);
|
|
6768
|
+
return from === null ? null : submoduleOf(from, binding.importedName, ctx);
|
|
6769
|
+
}
|
|
6770
|
+
function submoduleOf(base, name, ctx) {
|
|
6771
|
+
if (base.kind === "package") {
|
|
6772
|
+
return moduleNamed(
|
|
6773
|
+
base.from,
|
|
6774
|
+
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
6775
|
+
base.relativeLevel,
|
|
6776
|
+
ctx
|
|
6777
|
+
);
|
|
6503
6778
|
}
|
|
6504
|
-
return
|
|
6779
|
+
return base.file.file.endsWith("__init__.py") ? moduleNamed(base.file, name, 1, ctx) : null;
|
|
6505
6780
|
}
|
|
6506
|
-
function
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
6510
|
-
const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
|
|
6511
|
-
if (opened.kind !== "module") {
|
|
6512
|
-
continue;
|
|
6513
|
-
}
|
|
6514
|
-
const member = memberOfModule(opened.file, name, ctx, visited);
|
|
6515
|
-
if (member.kind !== "stop") {
|
|
6516
|
-
found.push(member);
|
|
6517
|
-
}
|
|
6781
|
+
function memberKey(base, name, ctx) {
|
|
6782
|
+
if (base.kind === "package") {
|
|
6783
|
+
return null;
|
|
6518
6784
|
}
|
|
6519
|
-
if (
|
|
6520
|
-
return
|
|
6785
|
+
if (base.file.module.moduleScope.bindings.has(name)) {
|
|
6786
|
+
return { kind: "key", key: `${base.file.file}#${name}` };
|
|
6521
6787
|
}
|
|
6522
|
-
return
|
|
6788
|
+
return openImportMember(base.file, name, ctx, /* @__PURE__ */ new Set());
|
|
6789
|
+
}
|
|
6790
|
+
function moduleNamed(from, module2, relativeLevel, ctx) {
|
|
6791
|
+
const resolution = resolveModule(
|
|
6792
|
+
from.file,
|
|
6793
|
+
{ module: module2, relativeLevel },
|
|
6794
|
+
{ roots: ctx.roots }
|
|
6795
|
+
);
|
|
6796
|
+
if (resolution.status !== "resolved") {
|
|
6797
|
+
return resolution.reason === "external" ? { kind: "package", dotted: module2, relativeLevel, from } : null;
|
|
6798
|
+
}
|
|
6799
|
+
const file = ctx.filesByPath.get(resolution.file);
|
|
6800
|
+
return file === void 0 ? null : { kind: "module", file };
|
|
6523
6801
|
}
|
|
6524
6802
|
|
|
6525
6803
|
// src/reach/closure.ts
|
|
6526
6804
|
var REACHABLE_RULES = [
|
|
6527
|
-
(0,
|
|
6528
|
-
(0,
|
|
6805
|
+
(0, import_datalog.rule)("reachable", [(0, import_datalog.variable)("f")], [(0, import_datalog.lit)("entry", (0, import_datalog.variable)("f"))]),
|
|
6806
|
+
(0, import_datalog.rule)(
|
|
6529
6807
|
"reachable",
|
|
6530
|
-
[(0,
|
|
6531
|
-
[(0,
|
|
6808
|
+
[(0, import_datalog.variable)("g")],
|
|
6809
|
+
[(0, import_datalog.lit)("reachable", (0, import_datalog.variable)("f")), (0, import_datalog.lit)("calls", (0, import_datalog.variable)("f"), (0, import_datalog.variable)("g"))]
|
|
6532
6810
|
)
|
|
6533
6811
|
];
|
|
6534
6812
|
function reachedFunctions(seeds, options) {
|
|
6535
6813
|
const ctx = {
|
|
6536
6814
|
filesByPath: new Map(options.files.map((file) => [file.file, file])),
|
|
6537
|
-
roots: options.roots
|
|
6815
|
+
roots: options.roots,
|
|
6816
|
+
facts: options.facts,
|
|
6817
|
+
definitions: options.definitions
|
|
6538
6818
|
};
|
|
6539
|
-
const db = new
|
|
6819
|
+
const db = new import_datalog.Database();
|
|
6540
6820
|
const functionByKey = /* @__PURE__ */ new Map();
|
|
6541
6821
|
const seedKeys = /* @__PURE__ */ new Set();
|
|
6542
6822
|
const scanned = /* @__PURE__ */ new Set();
|
|
@@ -6556,21 +6836,26 @@ function reachedFunctions(seeds, options) {
|
|
|
6556
6836
|
db.add("entry", [seed.key]);
|
|
6557
6837
|
}
|
|
6558
6838
|
for (; ; ) {
|
|
6559
|
-
(0,
|
|
6839
|
+
(0, import_datalog.evaluate)(db, REACHABLE_RULES);
|
|
6560
6840
|
const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
|
|
6561
6841
|
if (frontier.length === 0) {
|
|
6562
6842
|
break;
|
|
6563
6843
|
}
|
|
6564
|
-
|
|
6844
|
+
const bodies = frontier.flatMap((key) => {
|
|
6565
6845
|
scanned.add(key);
|
|
6566
6846
|
const source = functionByKey.get(key);
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6847
|
+
return source === void 0 ? [] : [{ key, source, ...bodyOf(source) }];
|
|
6848
|
+
});
|
|
6849
|
+
const spellings = calleeSpellings(
|
|
6850
|
+
bodies.flatMap((body) => body.written),
|
|
6851
|
+
ctx
|
|
6852
|
+
);
|
|
6853
|
+
for (const { key, source, body, written } of bodies) {
|
|
6570
6854
|
const scan = scanBody(
|
|
6571
6855
|
source,
|
|
6572
6856
|
ctx,
|
|
6573
|
-
recognizedCallIds(source.node, options.storageFor(source.file))
|
|
6857
|
+
recognizedCallIds(source.node, options.storageFor(source.file)),
|
|
6858
|
+
{ body, written, spellings }
|
|
6574
6859
|
);
|
|
6575
6860
|
if (scan.stops.length > 0) {
|
|
6576
6861
|
stopsByKey.set(key, scan.stops);
|
|
@@ -6636,7 +6921,18 @@ var EMPTY_SCAN = {
|
|
|
6636
6921
|
parameterCalls: [],
|
|
6637
6922
|
passedPositions: /* @__PURE__ */ new Set()
|
|
6638
6923
|
};
|
|
6639
|
-
function
|
|
6924
|
+
function bodyOf(source) {
|
|
6925
|
+
const { file, node } = source;
|
|
6926
|
+
const body = field(node, "body");
|
|
6927
|
+
if (body === null) {
|
|
6928
|
+
return { body, written: [] };
|
|
6929
|
+
}
|
|
6930
|
+
return {
|
|
6931
|
+
body,
|
|
6932
|
+
written: callsWritten(file, body, scopeAt2(file, node), keyOf(source))
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
function scanBody(source, ctx, recognized, read) {
|
|
6640
6936
|
const followed = [];
|
|
6641
6937
|
const stops = [];
|
|
6642
6938
|
const placements = new import_behavioral_ir4.TargetPlacements();
|
|
@@ -6645,12 +6941,12 @@ function scanBody(source, ctx, recognized) {
|
|
|
6645
6941
|
const seen = /* @__PURE__ */ new Set();
|
|
6646
6942
|
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
6647
6943
|
const { file, node } = source;
|
|
6648
|
-
const
|
|
6649
|
-
if (body === null) {
|
|
6944
|
+
const { written, spellings } = read;
|
|
6945
|
+
if (read.body === null) {
|
|
6650
6946
|
return EMPTY_SCAN;
|
|
6651
6947
|
}
|
|
6652
6948
|
const ownParameters = callParameterNames(node, source.exportPath.length > 1);
|
|
6653
|
-
const recordPassedArgs = (call, callee, calleeKey
|
|
6949
|
+
const recordPassedArgs = (call, callee, calleeKey) => {
|
|
6654
6950
|
const args = field(call, "arguments");
|
|
6655
6951
|
if (args === null) {
|
|
6656
6952
|
return;
|
|
@@ -6659,9 +6955,8 @@ function scanBody(source, ctx, recognized) {
|
|
|
6659
6955
|
if (arg === null || arg.type !== "identifier") {
|
|
6660
6956
|
return;
|
|
6661
6957
|
}
|
|
6662
|
-
const resolved =
|
|
6663
|
-
|
|
6664
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6958
|
+
const resolved = functionNamed(
|
|
6959
|
+
readKey(file.file, arg, enclosingFunction(arg)),
|
|
6665
6960
|
ctx
|
|
6666
6961
|
);
|
|
6667
6962
|
if (resolved === null) {
|
|
@@ -6681,13 +6976,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6681
6976
|
}
|
|
6682
6977
|
});
|
|
6683
6978
|
};
|
|
6684
|
-
const record = (call,
|
|
6979
|
+
const record = (call, site) => {
|
|
6685
6980
|
const callee = calleeText(call);
|
|
6686
|
-
const outcome = resolveCallee2(
|
|
6687
|
-
call,
|
|
6688
|
-
{ file, scope: where.scope, rebound: where.rebound },
|
|
6689
|
-
ctx
|
|
6690
|
-
);
|
|
6981
|
+
const outcome = resolveCallee2(call, site, ctx, spellings);
|
|
6691
6982
|
const placed = outcome.kind === "followed" ? {
|
|
6692
6983
|
file: outcome.target.file.displayPath,
|
|
6693
6984
|
span: spanOf(outcome.target.node)
|
|
@@ -6696,8 +6987,7 @@ function scanBody(source, ctx, recognized) {
|
|
|
6696
6987
|
recordPassedArgs(
|
|
6697
6988
|
call,
|
|
6698
6989
|
callee,
|
|
6699
|
-
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6700
|
-
where
|
|
6990
|
+
outcome.kind === "followed" ? keyOf(outcome.target) : null
|
|
6701
6991
|
);
|
|
6702
6992
|
if (outcome.kind === "stopped") {
|
|
6703
6993
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
@@ -6720,32 +7010,9 @@ function scanBody(source, ctx, recognized) {
|
|
|
6720
7010
|
followed.push(outcome.target);
|
|
6721
7011
|
}
|
|
6722
7012
|
};
|
|
6723
|
-
(
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
{
|
|
6727
|
-
at: (child, where) => {
|
|
6728
|
-
if (child.type === "call") {
|
|
6729
|
-
record(child, where);
|
|
6730
|
-
}
|
|
6731
|
-
},
|
|
6732
|
-
into: (child, where) => {
|
|
6733
|
-
if (child.type === "function_definition") {
|
|
6734
|
-
return import_extractor7.SKIP_CHILDREN;
|
|
6735
|
-
}
|
|
6736
|
-
if (child.type === "lambda") {
|
|
6737
|
-
return {
|
|
6738
|
-
scope: where.scope,
|
|
6739
|
-
rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
|
|
6740
|
-
};
|
|
6741
|
-
}
|
|
6742
|
-
return {
|
|
6743
|
-
scope: file.module.scopeFor.get(child.id) ?? where.scope,
|
|
6744
|
-
rebound: where.rebound
|
|
6745
|
-
};
|
|
6746
|
-
}
|
|
6747
|
-
}
|
|
6748
|
-
);
|
|
7013
|
+
for (const { call, site } of written) {
|
|
7014
|
+
record(call, site);
|
|
7015
|
+
}
|
|
6749
7016
|
return {
|
|
6750
7017
|
followed,
|
|
6751
7018
|
stops,
|
|
@@ -6755,6 +7022,18 @@ function scanBody(source, ctx, recognized) {
|
|
|
6755
7022
|
passedPositions
|
|
6756
7023
|
};
|
|
6757
7024
|
}
|
|
7025
|
+
function callsWritten(file, body, outer, owner) {
|
|
7026
|
+
const found = [];
|
|
7027
|
+
(0, import_extractor7.walkDescendants)(body, outer, {
|
|
7028
|
+
at: (child, scope) => {
|
|
7029
|
+
if (child.type === "call") {
|
|
7030
|
+
found.push({ call: child, site: { file, scope, owner } });
|
|
7031
|
+
}
|
|
7032
|
+
},
|
|
7033
|
+
into: (child, scope) => child.type === "function_definition" ? import_extractor7.SKIP_CHILDREN : file.module.scopeFor.get(child.id) ?? scope
|
|
7034
|
+
});
|
|
7035
|
+
return found;
|
|
7036
|
+
}
|
|
6758
7037
|
function scopeAt2(file, node) {
|
|
6759
7038
|
let current = node;
|
|
6760
7039
|
while (current !== null) {
|
|
@@ -6766,40 +7045,6 @@ function scopeAt2(file, node) {
|
|
|
6766
7045
|
}
|
|
6767
7046
|
return file.module.moduleScope;
|
|
6768
7047
|
}
|
|
6769
|
-
var REBINDING_FIELDS = {
|
|
6770
|
-
assignment: "left",
|
|
6771
|
-
augmented_assignment: "left",
|
|
6772
|
-
named_expression: "name",
|
|
6773
|
-
for_statement: "left",
|
|
6774
|
-
for_in_clause: "left",
|
|
6775
|
-
as_pattern: "alias"
|
|
6776
|
-
};
|
|
6777
|
-
function reboundNames(body) {
|
|
6778
|
-
const names = /* @__PURE__ */ new Set();
|
|
6779
|
-
(0, import_extractor7.walkDescendants)(body, null, {
|
|
6780
|
-
at: (node) => {
|
|
6781
|
-
const target = REBINDING_FIELDS[node.type];
|
|
6782
|
-
if (target === void 0 || boundByBinder(node, body)) {
|
|
6783
|
-
return;
|
|
6784
|
-
}
|
|
6785
|
-
const written = field(node, target);
|
|
6786
|
-
if (written !== null) {
|
|
6787
|
-
for (const name of identifiersUnder(written)) {
|
|
6788
|
-
names.add(name);
|
|
6789
|
-
}
|
|
6790
|
-
}
|
|
6791
|
-
},
|
|
6792
|
-
into: (node) => node.type === "function_definition" ? import_extractor7.SKIP_CHILDREN : null
|
|
6793
|
-
});
|
|
6794
|
-
return names;
|
|
6795
|
-
}
|
|
6796
|
-
function boundByBinder(node, body) {
|
|
6797
|
-
if (node.type !== "assignment") {
|
|
6798
|
-
return false;
|
|
6799
|
-
}
|
|
6800
|
-
const statement = node.parent;
|
|
6801
|
-
return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
|
|
6802
|
-
}
|
|
6803
7048
|
function identifiersUnder(node, found = []) {
|
|
6804
7049
|
if (node.type === "identifier") {
|
|
6805
7050
|
found.push(node.text);
|
|
@@ -6812,10 +7057,6 @@ function identifiersUnder(node, found = []) {
|
|
|
6812
7057
|
}
|
|
6813
7058
|
return found;
|
|
6814
7059
|
}
|
|
6815
|
-
function lambdaParameters(lambda) {
|
|
6816
|
-
const parameters = field(lambda, "parameters");
|
|
6817
|
-
return parameters === null ? [] : identifiersUnder(parameters);
|
|
6818
|
-
}
|
|
6819
7060
|
function libraryUnit(target, options) {
|
|
6820
7061
|
const { file, node, name, exportPath } = target;
|
|
6821
7062
|
const body = field(node, "body");
|
|
@@ -6966,6 +7207,18 @@ var PythonWrapperIndex = class {
|
|
|
6966
7207
|
ownRoutes = /* @__PURE__ */ new Map();
|
|
6967
7208
|
unitsByKey = /* @__PURE__ */ new Map();
|
|
6968
7209
|
unitsByFile = /* @__PURE__ */ new Map();
|
|
7210
|
+
/** The project function a module-level name in this file refers to, or null for anything else. */
|
|
7211
|
+
functionCalled(file, name) {
|
|
7212
|
+
if (this.options.facts === void 0) {
|
|
7213
|
+
return null;
|
|
7214
|
+
}
|
|
7215
|
+
return functionNamed(nameKeyIn(file, null, name), {
|
|
7216
|
+
filesByPath: this.filesByPath,
|
|
7217
|
+
roots: this.options.roots,
|
|
7218
|
+
facts: this.options.facts,
|
|
7219
|
+
definitions: this.options.definitions
|
|
7220
|
+
});
|
|
7221
|
+
}
|
|
6969
7222
|
register(covers, target, declared) {
|
|
6970
7223
|
const registered = this.registered(target, declared);
|
|
6971
7224
|
const table = covers.kind === "everyRoute" ? this.everyRoute : this.ownRoutes;
|
|
@@ -7012,20 +7265,12 @@ var PythonWrapperIndex = class {
|
|
|
7012
7265
|
if (declared.form.type !== "dependency") {
|
|
7013
7266
|
continue;
|
|
7014
7267
|
}
|
|
7015
|
-
const site = {
|
|
7016
|
-
file,
|
|
7017
|
-
scope: query.module.moduleScope,
|
|
7018
|
-
rebound: /* @__PURE__ */ new Set()
|
|
7019
|
-
};
|
|
7020
7268
|
const targets = [
|
|
7021
7269
|
...decoratorDependencies(query.classification, declared.form),
|
|
7022
7270
|
...parameterDependencies(query.definitionNode, declared.form)
|
|
7023
7271
|
];
|
|
7024
7272
|
for (const name of targets) {
|
|
7025
|
-
const target =
|
|
7026
|
-
filesByPath: this.filesByPath,
|
|
7027
|
-
roots: this.options.roots
|
|
7028
|
-
});
|
|
7273
|
+
const target = this.functionCalled(file.file, name);
|
|
7029
7274
|
if (target !== null) {
|
|
7030
7275
|
found.push(this.registered(target, declared));
|
|
7031
7276
|
}
|
|
@@ -7167,12 +7412,8 @@ function registerConstructorDependencies(file, declared, form, index) {
|
|
|
7167
7412
|
if (listed === void 0) {
|
|
7168
7413
|
continue;
|
|
7169
7414
|
}
|
|
7170
|
-
const site = { file, scope, rebound: /* @__PURE__ */ new Set() };
|
|
7171
7415
|
for (const name of dependencyNamesIn(listed.node, form)) {
|
|
7172
|
-
const target =
|
|
7173
|
-
filesByPath: index.filesByPath,
|
|
7174
|
-
roots: index.options.roots
|
|
7175
|
-
});
|
|
7416
|
+
const target = index.functionCalled(file.file, name);
|
|
7176
7417
|
if (target !== null) {
|
|
7177
7418
|
index.register(
|
|
7178
7419
|
coverageOf(match, file, declared.pack),
|
|
@@ -7412,6 +7653,31 @@ function reportUnresolvedProjectModules(options, db) {
|
|
|
7412
7653
|
}
|
|
7413
7654
|
}
|
|
7414
7655
|
}
|
|
7656
|
+
function emitModelQueryFacts(db, packs) {
|
|
7657
|
+
for (const pack of packs) {
|
|
7658
|
+
for (const model of pack.models ?? []) {
|
|
7659
|
+
for (const base of model.baseNames) {
|
|
7660
|
+
for (const method of model.givesBack) {
|
|
7661
|
+
db.add("givesBackOne", [base, method]);
|
|
7662
|
+
}
|
|
7663
|
+
for (const entry of model.entryMethods) {
|
|
7664
|
+
db.add("givesBackOneOfArgument", [
|
|
7665
|
+
base,
|
|
7666
|
+
entry.method,
|
|
7667
|
+
String(entry.argument)
|
|
7668
|
+
]);
|
|
7669
|
+
}
|
|
7670
|
+
}
|
|
7671
|
+
for (const entry of model.entryFunctions) {
|
|
7672
|
+
db.add("givesBackOneOfImport", [
|
|
7673
|
+
entry.module,
|
|
7674
|
+
entry.name,
|
|
7675
|
+
String(entry.argument)
|
|
7676
|
+
]);
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7415
7681
|
async function extractPythonProject(options) {
|
|
7416
7682
|
const timer = options.onTiming !== void 0 ? (0, import_extractor10.createTimer)() : (0, import_extractor10.noopTimer)();
|
|
7417
7683
|
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
@@ -7434,9 +7700,9 @@ async function extractPythonProject(options) {
|
|
|
7434
7700
|
options.onCacheDiagnostic?.(lookup.diagnostic);
|
|
7435
7701
|
if (lookup.kind === "hit") {
|
|
7436
7702
|
options.onTiming?.(timer.report());
|
|
7437
|
-
return { summaries: lookup.summaries, facts: new
|
|
7703
|
+
return { summaries: lookup.summaries, facts: new import_datalog2.Database() };
|
|
7438
7704
|
}
|
|
7439
|
-
const db = new
|
|
7705
|
+
const db = new import_datalog2.Database();
|
|
7440
7706
|
const summaries = [];
|
|
7441
7707
|
const gapHandling = options.gapHandling ?? "permissive";
|
|
7442
7708
|
const tallies = (0, import_extractor6.createPackTallies)(options.packs);
|
|
@@ -7459,8 +7725,9 @@ async function extractPythonProject(options) {
|
|
|
7459
7725
|
);
|
|
7460
7726
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
7461
7727
|
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
7728
|
+
const modelQueries = options.packs.flatMap((pack) => pack.models ?? []);
|
|
7462
7729
|
const discovers = options.packs.some((pack) => pack.discovery.length > 0);
|
|
7463
|
-
const needsValues = discovers || mountsRouters || storagePatterns.length > 0;
|
|
7730
|
+
const needsValues = discovers || mountsRouters || storagePatterns.length > 0 || modelQueries.length > 0;
|
|
7464
7731
|
const definitions = /* @__PURE__ */ new Map();
|
|
7465
7732
|
timer.time("discover", () => {
|
|
7466
7733
|
for (const { file, root, module: moduleBinding } of bound) {
|
|
@@ -7473,6 +7740,7 @@ async function extractPythonProject(options) {
|
|
|
7473
7740
|
if (needsValues) {
|
|
7474
7741
|
bindEvaluator(db, { files: bound, definitions });
|
|
7475
7742
|
}
|
|
7743
|
+
emitModelQueryFacts(db, options.packs);
|
|
7476
7744
|
});
|
|
7477
7745
|
reportUnresolvedProjectModules(options, db);
|
|
7478
7746
|
const couldMatch = timer.time(
|
|
@@ -7500,6 +7768,7 @@ async function extractPythonProject(options) {
|
|
|
7500
7768
|
packs: options.packs,
|
|
7501
7769
|
roots: options.roots,
|
|
7502
7770
|
facts: needsValues ? db : void 0,
|
|
7771
|
+
definitions,
|
|
7503
7772
|
storageFor
|
|
7504
7773
|
})
|
|
7505
7774
|
);
|
|
@@ -7583,7 +7852,9 @@ async function extractPythonProject(options) {
|
|
|
7583
7852
|
files: bound,
|
|
7584
7853
|
roots: options.roots,
|
|
7585
7854
|
gapHandling,
|
|
7586
|
-
storageFor
|
|
7855
|
+
storageFor,
|
|
7856
|
+
facts: db,
|
|
7857
|
+
definitions
|
|
7587
7858
|
})
|
|
7588
7859
|
);
|
|
7589
7860
|
for (const [key, owners] of summariesBySeed) {
|
|
@@ -7774,7 +8045,7 @@ function width(node) {
|
|
|
7774
8045
|
}
|
|
7775
8046
|
var PythonWhySession = class {
|
|
7776
8047
|
root;
|
|
7777
|
-
db = new
|
|
8048
|
+
db = new import_datalog3.Database();
|
|
7778
8049
|
locations = /* @__PURE__ */ new Map();
|
|
7779
8050
|
trees = /* @__PURE__ */ new Map();
|
|
7780
8051
|
constructor(options) {
|
|
@@ -7843,9 +8114,9 @@ var PythonWhySession = class {
|
|
|
7843
8114
|
* which the caller says in its own words.
|
|
7844
8115
|
*/
|
|
7845
8116
|
explain(value, options = {}) {
|
|
7846
|
-
return (0,
|
|
8117
|
+
return (0, import_resolution6.explainResolvedKey)({
|
|
7847
8118
|
db: this.db,
|
|
7848
|
-
rules:
|
|
8119
|
+
rules: import_resolution6.RESOLUTION_RULES,
|
|
7849
8120
|
key: readKey(value.file, value.node, enclosingFunction(value.node)),
|
|
7850
8121
|
locate: (key) => this.locate(key),
|
|
7851
8122
|
displayPath: (key) => this.displayPath(key),
|
|
@@ -7976,6 +8247,7 @@ async function pythonImportEvidence(options) {
|
|
|
7976
8247
|
emitValueFacts,
|
|
7977
8248
|
extractPythonProject,
|
|
7978
8249
|
findPythonFiles,
|
|
8250
|
+
nameKeyIn,
|
|
7979
8251
|
nodeId,
|
|
7980
8252
|
objectReturnedBy,
|
|
7981
8253
|
parsePython,
|