@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 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 statementOf2 = /* @__PURE__ */ new Map();
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
- statementOf2.set(call.id, statement);
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 = statementOf2.get(call.id);
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 import_resolution3 = require("@suss/resolution");
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
- const asked = new Set(db.facts("wanted").map((row) => String(row[0])));
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, nameKey) {
1527
- if (!db.facts("wantedOrigin").some((row) => String(row[0]) === nameKey)) {
1528
- db.add("wantedOrigin", [nameKey]);
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
- for (const key of keys) {
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
- if (enclosing !== null && parameterNames(enclosing).has(node.text)) {
1600
- return `${nodeId(filePath, enclosing)}#${node.text}`;
1601
- }
1602
- return nameId(filePath, node.text);
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 parameterNames(fn) {
1658
- return new Set(parameterList(fn));
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
- if (callee === null) {
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
- const enclosing = emitter.enclosing;
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: { funcKey, params: declared }
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
- const recordNested = (node) => {
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
- emitBinding(inside, child, receiver);
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 emitBinding(emitter, assignment2, receiver) {
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 memberKey = declaredMemberKey(emitter, member, classKey);
1939
+ const memberKey2 = declaredMemberKey(emitter, member, classKey);
1954
1940
  const name = field(member, "name");
1955
- if (memberKey !== null && name !== null) {
1956
- add(emitter, "holdsProperty", classKey, name.text, memberKey);
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 emitValueFacts(db, filePath, root) {
1962
- const emitter = { db, filePath, enclosing: null };
1963
- const declaresName = (child, key) => {
1964
- const name = field(child, "name");
1965
- if (name === null) {
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
- add(emitter, "binds", nameId(filePath, name.text), key);
1969
- add(emitter, "exportsAs", filePath, name.text, key);
2115
+ existing.push(write);
1970
2116
  };
1971
- const walk = (node) => {
1972
- for (const child of children(node)) {
1973
- if (child.type === "class_definition") {
1974
- declaresName(child, emitClassFacts(emitter, child));
1975
- continue;
1976
- }
1977
- if (isFunction(child)) {
1978
- declaresName(child, emitFunctionFacts(emitter, child));
1979
- }
1980
- if (child.type === "assignment") {
1981
- emitAssignment(emitter, child);
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
- walk(child);
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
- walk(root);
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 import_resolution2 = require("@suss/resolution");
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: statementOf,
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 statementOf(node) {
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, import_resolution2.nodeOfKey)(rootsByFile, answer);
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, import_resolution3.mountPathsOf)(mountEdgesOf(index), construction.valueKey);
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 nameKey = `${scan.bound.file}#${name}`;
3413
- const constructionKey2 = subjectConstructions(facts, [nameKey]).get(
3414
- nameKey
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 targetNames(node) {
3816
+ function targetNames2(node) {
3506
3817
  if (node.type === "identifier") {
3507
3818
  return [node.text];
3508
3819
  }
3509
- return bodyStatements(node).flatMap(targetNames);
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 targetNames(left)) {
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).filter(
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 import_datalog4 = require("@suss/datalog");
6091
- var import_resolution4 = require("@suss/resolution");
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 nameKey = `${filePath}#${localName}`;
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", [nameKey, moduleText, importedName]);
6431
+ db.add("pyImportedName", [nameKey2, moduleText, importedName]);
6126
6432
  if (scope.kind === "module") {
6127
- db.add("exportsAs", [filePath, localName, nameKey]);
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", [nameKey, resolution.file, importedName]);
6437
+ db.add("imports", [nameKey2, resolution.file, importedName]);
6132
6438
  continue;
6133
6439
  }
6134
- db.add("imports", [nameKey, moduleText, importedName]);
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 import_datalog3 = require("@suss/datalog");
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 import_datalog2 = require("@suss/datalog");
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 stop = (reason) => ({ kind: "stop", reason });
6279
- var MODULE_STOP = {
6280
- ambiguous: "multipleSources",
6281
- outsideRoots: "outsideRun"
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
- function resolveCallee2(call, site, ctx) {
6284
- const callee = field(call, "function");
6285
- if (callee === null) {
6286
- return { kind: "stopped", reason: "noDeclaration" };
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 asCallee(resolveExpression(callee, site, ctx), ctx);
6605
+ return { spellingOf, outcomes: (0, import_resolution5.calleeOutcomes)(ctx.facts, [...keys]) };
6289
6606
  }
6290
- function resolveNamedFunctionArgument(name, site, ctx) {
6291
- if (site.rebound.has(name)) {
6292
- return null;
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 binding = resolveName(site.scope, name);
6295
- if (binding !== null && binding.kind === "assignment") {
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 resolved = binding === null ? throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set()) : resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
6299
- return resolved.kind === "function" ? resolved.target : null;
6619
+ const outcome = (0, import_resolution5.calleeOutcomeOf)(ctx.facts, nameKey2);
6620
+ return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
6300
6621
  }
6301
- function asCallee(resolved, ctx) {
6302
- if (resolved.kind === "function") {
6303
- return { kind: "followed", target: resolved.target };
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
- var EXPRESSION_RESOLVERS = {
6320
- identifier: (node, site, ctx) => resolveIdentifier(node.text, site, ctx),
6321
- attribute: (node, site, ctx) => {
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 instanceBuiltBy(built, otherwise) {
6343
- if (built.kind === "class" || built.kind === "stop") {
6344
- return built;
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 built.kind === "package" ? stop("outsideRun") : stop(otherwise);
6638
+ return throughModules(callee, site, ctx) ?? {
6639
+ kind: "key",
6640
+ key: readKey(site.file.file, callee, enclosingFunction(callee))
6641
+ };
6347
6642
  }
6348
- function resolveIdentifier(name, site, ctx) {
6349
- if (site.rebound.has(name)) {
6350
- return stop("unsettledValue");
6643
+ function asCallee(outcome, owner, ctx) {
6644
+ if (outcome.kind === "function") {
6645
+ return functionCallee(outcome.key, ctx);
6351
6646
  }
6352
- if (isReceiver(name, site.scope)) {
6353
- return receiverClass(site);
6647
+ if (outcome.kind === "object") {
6648
+ return constructorOf(outcome.key, ctx);
6354
6649
  }
6355
- const binding = resolveName(site.scope, name);
6356
- if (binding === null) {
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 resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
6360
- }
6361
- function isReceiver(name, scope) {
6362
- return (name === "self" || name === "cls") && scope.kind === "function" && scope.parent?.kind === "class";
6653
+ return {
6654
+ kind: "stopped",
6655
+ reason: STOP_FOR[outcome.kind] ?? "noDeclaration"
6656
+ };
6363
6657
  }
6364
- function receiverClass(site) {
6365
- const classScope = site.scope.parent;
6366
- if (classScope === null || classScope === void 0) {
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
- return { kind: "class", file: site.file, node: classScope.node };
6662
+ const target = functionAt(key, ctx);
6663
+ return target === null ? NO_DECLARATION : { kind: "followed", target };
6370
6664
  }
6371
- var BINDING_RESOLVERS = {
6372
- functionDef: (binding, file) => ({
6373
- kind: "function",
6374
- target: reachedFunction(file, binding.node)
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
- if (value.type === "attribute") {
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 reachedFunction(file, node, owner) {
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 ownerName = owner === void 0 ? null : field(owner, "name")?.text;
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 === null || ownerName === void 0 ? [name] : [ownerName, name]
6685
+ exportPath: ownerName === void 0 ? [name] : [ownerName, name]
6440
6686
  };
6441
6687
  }
6442
- function moduleNamed(from, module2, relativeLevel, ctx) {
6443
- const resolution = resolveModule(
6444
- from.file,
6445
- { module: module2, relativeLevel },
6446
- { roots: ctx.roots }
6447
- );
6448
- if (resolution.status !== "resolved") {
6449
- return resolution.reason === "external" ? { kind: "package", dotted: module2, relativeLevel, from } : stop(MODULE_STOP[resolution.reason]);
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
- const file = ctx.filesByPath.get(resolution.file);
6452
- return file === void 0 ? stop("outsideRun") : { kind: "module", file };
6697
+ return null;
6453
6698
  }
6454
- function memberOf(base, name, ctx, visited = /* @__PURE__ */ new Set()) {
6455
- if (base.kind === "module") {
6456
- return memberOfModule(base.file, name, ctx, visited);
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 (base.kind === "package") {
6459
- return moduleNamed(
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
- if (base.kind === "class") {
6467
- return memberOfClass(base.file, base.node, name, ctx);
6706
+ const object = field(callee, "object");
6707
+ const attribute = field(callee, "attribute");
6708
+ if (object === null || attribute === null) {
6709
+ return null;
6468
6710
  }
6469
- if (base.kind === "stop") {
6470
- return base;
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
- return stop("noDeclaration");
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 memberOfClass(file, classNode, name, ctx) {
6475
- const classScope = file.module.scopeFor.get(classNode.id);
6476
- const binding = classScope?.bindings.get(name);
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 (binding.kind === "functionDef") {
6481
- return {
6482
- kind: "function",
6483
- target: reachedFunction(file, binding.node, classNode)
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
- return resolveBinding(binding, file, classScope, ctx, /* @__PURE__ */ new Set());
6749
+ const base = moduleAt(object, site, ctx);
6750
+ return base === null ? null : submoduleOf(base, attribute.text, ctx);
6487
6751
  }
6488
- function memberOfModule(file, name, ctx, visited) {
6489
- const key = `${file.file}#${name}`;
6490
- if (visited.has(key)) {
6491
- return stop("unsettledValue");
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
- visited.add(key);
6494
- const binding = file.module.moduleScope.bindings.get(name);
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
- if (file.file.endsWith("__init__.py")) {
6499
- const submodule = moduleNamed(file, name, 1, ctx);
6500
- if (submodule.kind === "module") {
6501
- return submodule;
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 throughOpenImports(file, name, ctx, visited);
6779
+ return base.file.file.endsWith("__init__.py") ? moduleNamed(base.file, name, 1, ctx) : null;
6505
6780
  }
6506
- function throughOpenImports(file, name, ctx, visited) {
6507
- const found = [];
6508
- for (const spec of file.module.openImports) {
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 (found.length > 1) {
6520
- return stop("multipleSources");
6785
+ if (base.file.module.moduleScope.bindings.has(name)) {
6786
+ return { kind: "key", key: `${base.file.file}#${name}` };
6521
6787
  }
6522
- return found[0] ?? stop("noDeclaration");
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, import_datalog2.rule)("reachable", [(0, import_datalog2.variable)("f")], [(0, import_datalog2.lit)("entry", (0, import_datalog2.variable)("f"))]),
6528
- (0, import_datalog2.rule)(
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, import_datalog2.variable)("g")],
6531
- [(0, import_datalog2.lit)("reachable", (0, import_datalog2.variable)("f")), (0, import_datalog2.lit)("calls", (0, import_datalog2.variable)("f"), (0, import_datalog2.variable)("g"))]
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 import_datalog2.Database();
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, import_datalog2.evaluate)(db, REACHABLE_RULES);
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
- for (const key of frontier) {
6844
+ const bodies = frontier.flatMap((key) => {
6565
6845
  scanned.add(key);
6566
6846
  const source = functionByKey.get(key);
6567
- if (source === void 0) {
6568
- continue;
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 scanBody(source, ctx, recognized) {
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 body = field(node, "body");
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, where) => {
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 = resolveNamedFunctionArgument(
6663
- arg.text,
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, where) => {
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
- (0, import_extractor7.walkDescendants)(
6724
- body,
6725
- { scope: scopeAt2(file, node), rebound: reboundNames(body) },
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 = resolveNamedFunctionArgument(name, site, {
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 = resolveNamedFunctionArgument(name, site, {
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 import_datalog3.Database() };
7703
+ return { summaries: lookup.summaries, facts: new import_datalog2.Database() };
7438
7704
  }
7439
- const db = new import_datalog3.Database();
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 import_datalog4.Database();
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, import_resolution4.explainResolvedKey)({
8117
+ return (0, import_resolution6.explainResolvedKey)({
7847
8118
  db: this.db,
7848
- rules: import_resolution4.RESOLUTION_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,