@suss/adapter-python 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs 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) {
@@ -159,6 +165,25 @@ function children(node) {
159
165
  function bodyStatements(body) {
160
166
  return children(body);
161
167
  }
168
+ function nestedStatements(stmt) {
169
+ const found = [];
170
+ for (const child of children(stmt)) {
171
+ if (child.type === "block") {
172
+ for (const inner of children(child)) {
173
+ if (inner.type.endsWith("_clause")) {
174
+ found.push(...nestedStatements(inner));
175
+ continue;
176
+ }
177
+ found.push(inner);
178
+ }
179
+ continue;
180
+ }
181
+ if (child.type.endsWith("_clause")) {
182
+ found.push(...nestedStatements(child));
183
+ }
184
+ }
185
+ return found;
186
+ }
162
187
  function stripDecorators(node) {
163
188
  if (node.type !== "decorated_definition") {
164
189
  return { definition: node, decorators: [] };
@@ -519,8 +544,36 @@ function resolveNonlocal(scope, name) {
519
544
  }
520
545
 
521
546
  // src/annotations.ts
522
- function createAnnotationContext(scopeFor) {
523
- return { scopeFor, definitions: /* @__PURE__ */ new Map() };
547
+ function createAnnotationContext(scopeFor, importedDefinition = null) {
548
+ return {
549
+ scopeMaps: [scopeFor],
550
+ definitions: /* @__PURE__ */ new Map(),
551
+ importedDefinition,
552
+ expanding: /* @__PURE__ */ new Set()
553
+ };
554
+ }
555
+ function scopeOfNode(ctx, node) {
556
+ for (const scopeFor of ctx.scopeMaps) {
557
+ const scope = scopeFor.get(node.id);
558
+ if (scope !== void 0) {
559
+ return scope;
560
+ }
561
+ }
562
+ return void 0;
563
+ }
564
+ function aliasValueOf(name, scope, ctx) {
565
+ const binding = resolveName(scope, name);
566
+ if (binding?.kind === "assignment") {
567
+ return binding.value === null ? null : { node: binding.value, scope };
568
+ }
569
+ if (binding?.kind !== "import" && binding?.kind !== "importFrom") {
570
+ return null;
571
+ }
572
+ const imported = ctx.importedDefinition?.(scope, name) ?? null;
573
+ if (imported === null || imported.node.type === "class_definition") {
574
+ return null;
575
+ }
576
+ return { node: imported.node, scope: imported.moduleScope };
524
577
  }
525
578
  function collectedDefinitions(ctx) {
526
579
  const out = {};
@@ -563,6 +616,7 @@ var EXPRESSION_SHAPERS = {
563
616
  identifier: (node, scope, ctx) => shapeFromName(node.text, scope, ctx),
564
617
  none: () => ({ type: "null" }),
565
618
  generic_type: shapeFromGenericType,
619
+ subscript: shapeFromSubscript,
566
620
  binary_operator: shapeFromBinaryOperator
567
621
  };
568
622
  function shapeFromExpression(node, scope, ctx) {
@@ -584,8 +638,46 @@ function shapeFromName(name, scope, ctx) {
584
638
  if (binding?.kind === "classDef") {
585
639
  return recordShapeRef(name, binding.node, ctx);
586
640
  }
641
+ if (binding?.kind === "import" || binding?.kind === "importFrom") {
642
+ const imported = ctx.importedDefinition?.(scope, name) ?? null;
643
+ if (imported?.node.type === "class_definition") {
644
+ if (!ctx.scopeMaps.includes(imported.scopeFor)) {
645
+ ctx.scopeMaps.push(imported.scopeFor);
646
+ }
647
+ return recordShapeRef(name, imported.node, ctx);
648
+ }
649
+ }
650
+ const alias = aliasValueOf(name, scope, ctx);
651
+ if (alias !== null) {
652
+ return shapeFromAliasValue(name, alias, ctx);
653
+ }
587
654
  return { type: "ref", name };
588
655
  }
656
+ function shapeFromAliasValue(name, alias, ctx) {
657
+ if (ctx.expanding.has(alias.node.id)) {
658
+ return { type: "ref", name };
659
+ }
660
+ ctx.expanding.add(alias.node.id);
661
+ try {
662
+ return shapeFromExpression(alias.node, alias.scope, ctx);
663
+ } finally {
664
+ ctx.expanding.delete(alias.node.id);
665
+ }
666
+ }
667
+ function shapeFromSubscript(node, scope, ctx) {
668
+ const base = field(node, "value");
669
+ const args = fields(node, "subscript");
670
+ const baseName = base?.type === "identifier" ? base.text : null;
671
+ if (baseName === "Annotated") {
672
+ const first = args[0];
673
+ return first === void 0 ? { type: "unknown" } : shapeFromExpression(first, scope, ctx);
674
+ }
675
+ return shapeFromGeneric(
676
+ baseName,
677
+ node.text,
678
+ args.map((arg) => shapeFromExpression(arg, scope, ctx))
679
+ );
680
+ }
589
681
  function genericTypeArgs(node) {
590
682
  const typeParameter = node.namedChildren.find(
591
683
  (child) => child !== null && child.type === "type_parameter"
@@ -596,14 +688,20 @@ function genericTypeArgs(node) {
596
688
  }
597
689
  function shapeFromGenericType(node, scope, ctx) {
598
690
  const base = node.namedChild(0);
599
- const args = genericTypeArgs(node);
600
- const shapeOf = (arg) => arg !== void 0 ? annotationToShape(arg, scope, ctx) : { type: "unknown" };
601
691
  const baseName = base?.type === "identifier" ? base.text : null;
692
+ return shapeFromGeneric(
693
+ baseName,
694
+ node.text,
695
+ genericTypeArgs(node).map((arg) => annotationToShape(arg, scope, ctx))
696
+ );
697
+ }
698
+ function shapeFromGeneric(baseName, text2, args) {
699
+ const shapeOf = (arg) => arg ?? { type: "unknown" };
602
700
  if (baseName === "Optional") {
603
701
  return { type: "union", variants: [shapeOf(args[0]), { type: "null" }] };
604
702
  }
605
703
  if (baseName === "Union") {
606
- return { type: "union", variants: args.map((arg) => shapeOf(arg)) };
704
+ return { type: "union", variants: args };
607
705
  }
608
706
  if (baseName !== null && LIST_NAMES.has(baseName)) {
609
707
  return { type: "array", items: shapeOf(args[0]) };
@@ -614,7 +712,7 @@ function shapeFromGenericType(node, scope, ctx) {
614
712
  if (baseName !== null && TUPLE_SET_NAMES.has(baseName)) {
615
713
  return { type: "array", items: shapeOf(args[0]) };
616
714
  }
617
- return { type: "ref", name: baseName ?? node.text };
715
+ return { type: "ref", name: baseName ?? text2 };
618
716
  }
619
717
  function shapeFromBinaryOperator(node, scope, ctx) {
620
718
  const operator = field(node, "operator");
@@ -639,7 +737,7 @@ function recordShapeRef(name, classNode, ctx) {
639
737
  }
640
738
  ctx.definitions.set(key, null);
641
739
  const bodyNode = field(classNode, "body");
642
- const classScope = ctx.scopeFor.get(classNode.id);
740
+ const classScope = scopeOfNode(ctx, classNode);
643
741
  ctx.definitions.set(
644
742
  key,
645
743
  bodyNode !== null && classScope !== void 0 ? recordShapeOf(bodyNode, classScope, ctx) : { type: "record", properties: {} }
@@ -1372,14 +1470,14 @@ function invocationEffects(definitionNode) {
1372
1470
  if (calls.length === 0) {
1373
1471
  return [];
1374
1472
  }
1375
- const statementOf2 = /* @__PURE__ */ new Map();
1473
+ const statementOf3 = /* @__PURE__ */ new Map();
1376
1474
  const statements = [];
1377
1475
  for (const call of calls) {
1378
1476
  const statement = enclosingStatement(call, body);
1379
1477
  if (statement === null) {
1380
1478
  continue;
1381
1479
  }
1382
- statementOf2.set(call.id, statement);
1480
+ statementOf3.set(call.id, statement);
1383
1481
  statements.push(statement);
1384
1482
  }
1385
1483
  const lowered = lowerPythonBody(body, statements);
@@ -1391,7 +1489,7 @@ function invocationEffects(definitionNode) {
1391
1489
  statements
1392
1490
  );
1393
1491
  return calls.map((call) => {
1394
- const statement = statementOf2.get(call.id);
1492
+ const statement = statementOf3.get(call.id);
1395
1493
  const conditions = (0, import_extractor3.sharedGatingConditions)(
1396
1494
  statement === void 0 ? void 0 : enumerated.byTerminal.get(statement),
1397
1495
  predicateOf
@@ -1407,40 +1505,28 @@ function invocationEffects(definitionNode) {
1407
1505
  }
1408
1506
 
1409
1507
  // src/routers.ts
1410
- var import_resolution3 = require("@suss/resolution");
1508
+ var import_resolution4 = require("@suss/resolution");
1411
1509
 
1412
1510
  // src/facts/resolve.ts
1413
- var import_datalog = require("@suss/datalog");
1414
1511
  var import_resolution = require("@suss/resolution");
1415
- var RESOLUTION_PROGRAM = (0, import_datalog.deriveOnDemand)(
1416
- [...import_resolution.RESOLUTION_RULES, ...import_resolution.RESOLUTION_QUESTIONS],
1417
- import_resolution.ANSWER_RELATIONS
1418
- );
1419
1512
  function resolveCalls(db, callKeys) {
1420
- const asked = new Set(db.facts("wanted").map((row) => String(row[0])));
1421
- const fresh = callKeys.filter((key) => !asked.has(key));
1422
- if (fresh.length === 0) {
1423
- return;
1424
- }
1425
- for (const callKey of fresh) {
1426
- db.add("wanted", [callKey]);
1427
- }
1428
- (0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
1513
+ (0, import_resolution.askResolution)(db, callKeys);
1429
1514
  }
1430
1515
  function writtenValueOf(db, key) {
1431
1516
  resolveCalls(db, [key]);
1432
1517
  return (0, import_resolution.writtenValueOf)(db, key, (keys) => resolveCalls(db, keys));
1433
1518
  }
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]) }));
1522
+ }
1434
1523
  function subjectConstructions(db, valueKeys) {
1435
1524
  const found = /* @__PURE__ */ new Map();
1436
1525
  if (valueKeys.length === 0) {
1437
1526
  return found;
1438
1527
  }
1439
1528
  const askSubjects = (keys) => {
1440
- for (const key of keys) {
1441
- db.add("wantedSubject", [key]);
1442
- }
1443
- (0, import_datalog.evaluate)(db, RESOLUTION_PROGRAM.rules);
1529
+ (0, import_resolution.askResolution)(db, keys, "wantedSubject");
1444
1530
  };
1445
1531
  askSubjects(valueKeys);
1446
1532
  const placeholders = (0, import_resolution.placeholderValues)(db);
@@ -1487,6 +1573,7 @@ function objectReturnedBy(db, callKey) {
1487
1573
  }
1488
1574
 
1489
1575
  // src/facts/values.ts
1576
+ var import_resolution2 = require("@suss/resolution");
1490
1577
  function nodeId(filePath, node) {
1491
1578
  return `${filePath}:${node.startIndex}-${node.endIndex}`;
1492
1579
  }
@@ -1497,10 +1584,10 @@ function readKey(filePath, node, enclosing) {
1497
1584
  if (node.type !== "identifier") {
1498
1585
  return nodeId(filePath, node);
1499
1586
  }
1500
- if (enclosing !== null && parameterNames(enclosing).has(node.text)) {
1501
- return `${nodeId(filePath, enclosing)}#${node.text}`;
1502
- }
1503
- 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);
1504
1591
  }
1505
1592
  var WRITTEN_VALUE_TYPES = /* @__PURE__ */ new Set([
1506
1593
  "string",
@@ -1538,9 +1625,6 @@ function parameterName2(param) {
1538
1625
  }
1539
1626
  return children(param).find((child) => child.type === "identifier") ?? null;
1540
1627
  }
1541
- function parameterList(fn) {
1542
- return parameterShapes(fn).map((parameter) => parameter.name);
1543
- }
1544
1628
  function parameterShapes(fn) {
1545
1629
  const params = field(fn, "parameters");
1546
1630
  const declared = [];
@@ -1555,24 +1639,52 @@ function parameterShapes(fn) {
1555
1639
  }
1556
1640
  return declared;
1557
1641
  }
1558
- function parameterNames(fn) {
1559
- 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;
1560
1652
  }
1561
1653
  function add(emitter, relation, ...tuple) {
1562
1654
  emitter.db.add(relation, tuple);
1563
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
+ }
1564
1679
  function emitCall(emitter, call) {
1565
1680
  add(emitter, "writtenValue", nodeId(emitter.filePath, call));
1566
1681
  const callee = field(call, "function");
1567
- if (callee === null) {
1682
+ const args = field(call, "arguments");
1683
+ if (callee === null || args === null) {
1568
1684
  return;
1569
1685
  }
1570
1686
  const callKey = nodeId(emitter.filePath, call);
1571
1687
  add(emitter, "call", callKey, valueKey(emitter, callee));
1572
- const args = field(call, "arguments");
1573
- if (args === null) {
1574
- return;
1575
- }
1576
1688
  let position = 0;
1577
1689
  for (const argument of children(args)) {
1578
1690
  if (argument.type === "keyword_argument") {
@@ -1603,11 +1715,7 @@ function valueKey(emitter, value) {
1603
1715
  if (value.type !== "identifier") {
1604
1716
  return nodeId(emitter.filePath, value);
1605
1717
  }
1606
- const enclosing = emitter.enclosing;
1607
- if (enclosing !== null && enclosing.params.has(value.text)) {
1608
- return `${enclosing.funcKey}#${value.text}`;
1609
- }
1610
- return nameId(emitter.filePath, value.text);
1718
+ return nameKey(emitter.filePath, emitter.enclosing, value.text);
1611
1719
  }
1612
1720
  function emitSequence(emitter, sequence) {
1613
1721
  const objectKey = nodeId(emitter.filePath, sequence);
@@ -1697,7 +1805,6 @@ function emitFunctionFacts(emitter, fn, classKey) {
1697
1805
  const funcKey = nodeId(emitter.filePath, fn);
1698
1806
  add(emitter, "func", funcKey);
1699
1807
  const params = field(fn, "parameters");
1700
- const declared = parameterNames(fn);
1701
1808
  let position = classKey === void 0 ? 0 : -1;
1702
1809
  let byPosition = true;
1703
1810
  let receiver = null;
@@ -1720,24 +1827,22 @@ function emitFunctionFacts(emitter, fn, classKey) {
1720
1827
  }
1721
1828
  position += 1;
1722
1829
  }
1830
+ const reading = scopeReadingOf(fn);
1723
1831
  const inside = {
1724
1832
  ...emitter,
1725
- enclosing: { funcKey, params: declared }
1833
+ enclosing: {
1834
+ funcKey,
1835
+ locals: reading.locals,
1836
+ globals: reading.globals,
1837
+ parent: emitter.enclosing
1838
+ }
1726
1839
  };
1840
+ emitScopeWrites(inside, reading, false);
1727
1841
  const body = field(fn, "body");
1728
1842
  if (body === null) {
1729
1843
  return funcKey;
1730
1844
  }
1731
- const recordNested = (node) => {
1732
- for (const child of children(node)) {
1733
- if (isFunction(child)) {
1734
- add(emitter, "containsFn", funcKey, nodeId(emitter.filePath, child));
1735
- continue;
1736
- }
1737
- recordNested(child);
1738
- }
1739
- };
1740
- recordNested(body);
1845
+ emitNestedDefinitions(inside, body);
1741
1846
  walkExpressions(inside, body, (child) => {
1742
1847
  if (child.type === "return_statement") {
1743
1848
  const returned = child.namedChildren[0];
@@ -1748,29 +1853,17 @@ function emitFunctionFacts(emitter, fn, classKey) {
1748
1853
  if (child.type === "call") {
1749
1854
  add(inside, "bodyCalls", funcKey, nodeId(inside.filePath, child));
1750
1855
  }
1751
- if (child.type === "assignment") {
1752
- emitBinding(inside, child, receiver);
1856
+ if (child.type === "assignment" && receiver !== null) {
1857
+ emitReceiverProperty(inside, child, receiver);
1753
1858
  }
1754
1859
  emitExpressionFact(inside, child);
1755
1860
  });
1756
1861
  return funcKey;
1757
1862
  }
1758
- function emitBinding(emitter, assignment2, receiver) {
1863
+ function emitReceiverProperty(emitter, assignment2, receiver) {
1759
1864
  const left = field(assignment2, "left");
1760
1865
  const right = field(assignment2, "right");
1761
- if (left === null || right === null) {
1762
- return;
1763
- }
1764
- if (left.type === "identifier") {
1765
- add(
1766
- emitter,
1767
- "binds",
1768
- nameId(emitter.filePath, left.text),
1769
- valueKey(emitter, right)
1770
- );
1771
- return;
1772
- }
1773
- if (receiver === null || left.type !== "attribute") {
1866
+ if (left === null || right === null || left.type !== "attribute") {
1774
1867
  return;
1775
1868
  }
1776
1869
  const object = field(left, "object");
@@ -1786,20 +1879,13 @@ function emitBinding(emitter, assignment2, receiver) {
1786
1879
  valueKey(emitter, right)
1787
1880
  );
1788
1881
  }
1789
- function emitAssignment(emitter, assignment2) {
1790
- emitBinding(emitter, assignment2, null);
1791
- const left = field(assignment2, "left");
1792
- const right = field(assignment2, "right");
1793
- if (left === null || right === null || left.type !== "identifier") {
1794
- return;
1882
+ function assignedValue(right) {
1883
+ if (right.type !== "subscript") {
1884
+ return right;
1795
1885
  }
1796
- add(
1797
- emitter,
1798
- "exportsAs",
1799
- emitter.filePath,
1800
- left.text,
1801
- valueKey(emitter, right)
1802
- );
1886
+ const outer = field(right, "value");
1887
+ const first = fields(right, "subscript")[0];
1888
+ return outer?.text === "Annotated" && first !== void 0 ? first : right;
1803
1889
  }
1804
1890
  function declaredBy(statement) {
1805
1891
  if (statement.type === "decorated_definition") {
@@ -1843,40 +1929,361 @@ function emitClassFacts(emitter, cls) {
1843
1929
  }
1844
1930
  continue;
1845
1931
  }
1846
- const memberKey = declaredMemberKey(emitter, member, classKey);
1932
+ const memberKey2 = declaredMemberKey(emitter, member, classKey);
1847
1933
  const name = field(member, "name");
1848
- if (memberKey !== null && name !== null) {
1849
- add(emitter, "holdsProperty", classKey, name.text, memberKey);
1934
+ if (memberKey2 !== null && name !== null) {
1935
+ add(emitter, "holdsProperty", classKey, name.text, memberKey2);
1850
1936
  }
1851
1937
  }
1852
1938
  return classKey;
1853
1939
  }
1854
- function emitValueFacts(db, filePath, root) {
1855
- const emitter = { db, filePath, enclosing: null };
1856
- const declaresName = (child, key) => {
1857
- const name = field(child, "name");
1858
- if (name === null) {
1940
+ function emitNestedDefinitions(emitter, node) {
1941
+ for (const child of children(node)) {
1942
+ if (isFunction(child)) {
1943
+ emitFunctionFacts(emitter, child);
1944
+ if (emitter.enclosing !== null) {
1945
+ add(
1946
+ emitter,
1947
+ "containsFn",
1948
+ emitter.enclosing.funcKey,
1949
+ nodeId(emitter.filePath, child)
1950
+ );
1951
+ }
1952
+ continue;
1953
+ }
1954
+ if (child.type === "class_definition") {
1955
+ emitClassFacts(emitter, child);
1956
+ continue;
1957
+ }
1958
+ emitNestedDefinitions(emitter, child);
1959
+ }
1960
+ }
1961
+ var readingsByTree = /* @__PURE__ */ new WeakMap();
1962
+ function scopeReadingOf(fn) {
1963
+ const tree = fn.tree;
1964
+ let byNode = readingsByTree.get(tree);
1965
+ if (byNode === void 0) {
1966
+ byNode = /* @__PURE__ */ new Map();
1967
+ readingsByTree.set(tree, byNode);
1968
+ }
1969
+ const remembered = byNode.get(fn.id);
1970
+ if (remembered !== void 0) {
1971
+ return remembered;
1972
+ }
1973
+ const body = field(fn, "body");
1974
+ const reading = readScope(
1975
+ body !== null && body.type === "block" ? body : null,
1976
+ boundParameterNames(fn)
1977
+ );
1978
+ byNode.set(fn.id, reading);
1979
+ return reading;
1980
+ }
1981
+ var TARGET_PATTERN_TYPES = /* @__PURE__ */ new Set([
1982
+ "pattern_list",
1983
+ "tuple_pattern",
1984
+ "list_pattern"
1985
+ ]);
1986
+ function targetNames(target, found = []) {
1987
+ if (target.type === "identifier") {
1988
+ found.push(target);
1989
+ return found;
1990
+ }
1991
+ if (TARGET_PATTERN_TYPES.has(target.type)) {
1992
+ for (const child of children(target)) {
1993
+ targetNames(child, found);
1994
+ }
1995
+ }
1996
+ return found;
1997
+ }
1998
+ function readAssignment(node, sink) {
1999
+ const left = field(node, "left");
2000
+ const right = field(node, "right");
2001
+ if (left === null) {
2002
+ return;
2003
+ }
2004
+ const written = right === null ? null : assignedValue(right);
2005
+ const names = targetNames(left);
2006
+ for (const name of names) {
2007
+ sink.record(name.text, {
2008
+ // An unpacked target takes one piece of the value, and which piece
2009
+ // is not something the assignment says.
2010
+ value: names.length === 1 ? written : null,
2011
+ given: null,
2012
+ at: node,
2013
+ direct: isDirectStatement(node, sink.bodyOwner)
2014
+ });
2015
+ }
2016
+ }
2017
+ function readAugmentedAssignment(node, sink) {
2018
+ const left = field(node, "left");
2019
+ if (left === null || left.type !== "identifier") {
2020
+ return;
2021
+ }
2022
+ sink.record(left.text, {
2023
+ value: null,
2024
+ given: null,
2025
+ at: node,
2026
+ direct: isDirectStatement(node, sink.bodyOwner)
2027
+ });
2028
+ }
2029
+ function readIterationTarget(node, sink) {
2030
+ const left = field(node, "left");
2031
+ if (left === null) {
2032
+ return;
2033
+ }
2034
+ for (const name of targetNames(left)) {
2035
+ sink.record(name.text, {
2036
+ value: null,
2037
+ given: null,
2038
+ at: node,
2039
+ direct: false
2040
+ });
2041
+ }
2042
+ }
2043
+ function identifierOf(node) {
2044
+ if (node.type === "identifier") {
2045
+ return node;
2046
+ }
2047
+ return children(node).find((child) => child.type === "identifier") ?? null;
2048
+ }
2049
+ function readAsPattern(node, sink) {
2050
+ const target = field(node, "alias") ?? children(node)[1];
2051
+ const name = target === void 0 ? null : identifierOf(target);
2052
+ if (name === null) {
2053
+ return;
2054
+ }
2055
+ sink.record(name.text, {
2056
+ value: null,
2057
+ given: null,
2058
+ at: node,
2059
+ direct: false
2060
+ });
2061
+ }
2062
+ function readWalrus(node, sink) {
2063
+ const name = field(node, "name");
2064
+ if (name === null || name.type !== "identifier") {
2065
+ return;
2066
+ }
2067
+ sink.record(name.text, {
2068
+ value: field(node, "value"),
2069
+ given: null,
2070
+ at: node,
2071
+ direct: false
2072
+ });
2073
+ }
2074
+ function readGlobalStatement(node, sink) {
2075
+ for (const child of children(node)) {
2076
+ if (child.type === "identifier") {
2077
+ sink.globals.add(child.text);
2078
+ }
2079
+ }
2080
+ }
2081
+ function readNonlocalStatement(node, sink) {
2082
+ for (const child of children(node)) {
2083
+ if (child.type === "identifier") {
2084
+ sink.nonlocals.add(child.text);
2085
+ }
2086
+ }
2087
+ }
2088
+ var WRITE_READERS = {
2089
+ assignment: readAssignment,
2090
+ augmented_assignment: readAugmentedAssignment,
2091
+ for_statement: readIterationTarget,
2092
+ for_in_clause: readIterationTarget,
2093
+ as_pattern: readAsPattern,
2094
+ named_expression: readWalrus,
2095
+ global_statement: readGlobalStatement,
2096
+ nonlocal_statement: readNonlocalStatement
2097
+ };
2098
+ function readScope(bodyOwner, parameterNames) {
2099
+ const writes = /* @__PURE__ */ new Map();
2100
+ const globals = /* @__PURE__ */ new Set();
2101
+ const nonlocals = /* @__PURE__ */ new Set();
2102
+ const record = (name, write) => {
2103
+ const existing = writes.get(name);
2104
+ if (existing === void 0) {
2105
+ writes.set(name, [write]);
1859
2106
  return;
1860
2107
  }
1861
- add(emitter, "binds", nameId(filePath, name.text), key);
1862
- add(emitter, "exportsAs", filePath, name.text, key);
2108
+ existing.push(write);
1863
2109
  };
1864
- const walk = (node) => {
1865
- for (const child of children(node)) {
1866
- if (child.type === "class_definition") {
1867
- declaresName(child, emitClassFacts(emitter, child));
1868
- continue;
1869
- }
1870
- if (isFunction(child)) {
1871
- declaresName(child, emitFunctionFacts(emitter, child));
1872
- }
1873
- if (child.type === "assignment") {
1874
- emitAssignment(emitter, child);
2110
+ const sink = { bodyOwner, globals, nonlocals, record };
2111
+ const visit = (node) => {
2112
+ if (isFunction(node) || node.type === "class_definition") {
2113
+ const name = field(node, "name");
2114
+ if (name !== null) {
2115
+ record(name.text, {
2116
+ value: node,
2117
+ given: null,
2118
+ at: node,
2119
+ direct: isDirectStatement(node, bodyOwner)
2120
+ });
1875
2121
  }
1876
- walk(child);
2122
+ return;
2123
+ }
2124
+ WRITE_READERS[node.type]?.(node, sink);
2125
+ for (const child of children(node)) {
2126
+ visit(child);
1877
2127
  }
1878
2128
  };
1879
- walk(root);
2129
+ for (const statement of bodyOwner === null ? [] : children(bodyOwner)) {
2130
+ visit(statement);
2131
+ }
2132
+ const locals = new Set(writes.keys());
2133
+ for (const name of [...globals, ...nonlocals]) {
2134
+ locals.delete(name);
2135
+ }
2136
+ for (const name of parameterNames) {
2137
+ locals.add(name);
2138
+ addParameterWrite(writes.get(name), name);
2139
+ }
2140
+ return { bodyOwner, writes, locals, globals };
2141
+ }
2142
+ function addParameterWrite(later, name) {
2143
+ const first = later?.[0];
2144
+ if (later === void 0 || first === void 0) {
2145
+ return;
2146
+ }
2147
+ later.unshift({ value: null, given: name, at: first.at, direct: false });
2148
+ }
2149
+ function statementOf(node) {
2150
+ let current = node;
2151
+ while (current !== null) {
2152
+ const parent = current.parent;
2153
+ if (parent === null) {
2154
+ return null;
2155
+ }
2156
+ if (parent.type === "block" || parent.type === "module") {
2157
+ return current;
2158
+ }
2159
+ current = parent;
2160
+ }
2161
+ return null;
2162
+ }
2163
+ function isDirectStatement(node, bodyOwner) {
2164
+ if (bodyOwner === null) {
2165
+ return false;
2166
+ }
2167
+ const statement = statementOf(node);
2168
+ return statement !== null && statement.parent?.id === bodyOwner.id;
2169
+ }
2170
+ var UNREAD_PARENTS = /* @__PURE__ */ new Set([
2171
+ "as_pattern_target",
2172
+ "global_statement",
2173
+ "nonlocal_statement",
2174
+ "pattern_list",
2175
+ "tuple_pattern",
2176
+ "list_pattern",
2177
+ "dotted_name"
2178
+ ]);
2179
+ var UNREAD_FIELDS = {
2180
+ assignment: "left",
2181
+ augmented_assignment: "left",
2182
+ for_statement: "left",
2183
+ for_in_clause: "left",
2184
+ named_expression: "name",
2185
+ function_definition: "name",
2186
+ class_definition: "name",
2187
+ attribute: "attribute",
2188
+ keyword_argument: "name"
2189
+ };
2190
+ function isNameRead(name) {
2191
+ const parent = name.parent;
2192
+ if (parent === null) {
2193
+ return true;
2194
+ }
2195
+ if (UNREAD_PARENTS.has(parent.type)) {
2196
+ return false;
2197
+ }
2198
+ const fieldName = UNREAD_FIELDS[parent.type];
2199
+ return fieldName === void 0 || field(parent, fieldName)?.id !== name.id;
2200
+ }
2201
+ var READ_FIRST = {
2202
+ call: "function",
2203
+ attribute: "object"
2204
+ };
2205
+ function readFirst(node) {
2206
+ if (node.type === "parenthesized_expression") {
2207
+ return children(node)[0] ?? null;
2208
+ }
2209
+ const fieldName = READ_FIRST[node.type];
2210
+ return fieldName === void 0 ? null : field(node, fieldName);
2211
+ }
2212
+ var NAME_READS = {
2213
+ nameTypes: /* @__PURE__ */ new Set(["identifier"]),
2214
+ laterBodies: LATER_BODY_TYPES,
2215
+ childrenOf: children,
2216
+ isRead: isNameRead,
2217
+ readFirst
2218
+ };
2219
+ var CONSTRUCTION_TYPES = /* @__PURE__ */ new Set([
2220
+ "call",
2221
+ "list",
2222
+ "dictionary",
2223
+ "set",
2224
+ "tuple",
2225
+ "list_comprehension",
2226
+ "dictionary_comprehension",
2227
+ "set_comprehension",
2228
+ "generator_expression"
2229
+ ]);
2230
+ function describeWrite(emitter, write, name) {
2231
+ if (write.value === null) {
2232
+ return {
2233
+ value: write.given,
2234
+ placeholder: false,
2235
+ construction: null,
2236
+ narrowsName: false
2237
+ };
2238
+ }
2239
+ return {
2240
+ value: valueKey(emitter, write.value),
2241
+ placeholder: write.value.type === "none",
2242
+ construction: CONSTRUCTION_TYPES.has(write.value.type) ? write.value.text.replace(/\s+/g, " ").trim() : null,
2243
+ narrowsName: write.value.type === "call" && (0, import_resolution2.startsAtName)(write.value, name, NAME_READS)
2244
+ };
2245
+ }
2246
+ function settledValue(emitter, reading, name, writes) {
2247
+ const only = writes.length === 1 ? writes[0] : void 0;
2248
+ if (only !== void 0) {
2249
+ return only.value === null ? only.given : valueKey(emitter, only.value);
2250
+ }
2251
+ return (0, import_resolution2.valueLeftByWrites)(
2252
+ writes.map((write) => describeWrite(emitter, write, name)),
2253
+ (0, import_resolution2.writesRunInOrder)(reading.bodyOwner, name, writes, NAME_READS)
2254
+ );
2255
+ }
2256
+ function emitScopeWrites(emitter, reading, atModule) {
2257
+ for (const [name, writes] of reading.writes) {
2258
+ const settled = settledValue(emitter, reading, name, writes);
2259
+ const key = nameKey(emitter.filePath, emitter.enclosing, name);
2260
+ if (settled === null) {
2261
+ emitCandidates(emitter, key, name, writes);
2262
+ continue;
2263
+ }
2264
+ add(emitter, writes.length === 1 ? "binds" : "endsHolding", key, settled);
2265
+ if (atModule) {
2266
+ add(emitter, "exportsAs", emitter.filePath, name, settled);
2267
+ }
2268
+ }
2269
+ }
2270
+ function emitCandidates(emitter, key, name, writes) {
2271
+ for (const write of writes) {
2272
+ if (write.value === null) {
2273
+ if (write.given === null) {
2274
+ add(emitter, "writesUnstated", key);
2275
+ }
2276
+ continue;
2277
+ }
2278
+ if (!describeWrite(emitter, write, name).narrowsName) {
2279
+ add(emitter, "mayHold", key, valueKey(emitter, write.value));
2280
+ }
2281
+ }
2282
+ }
2283
+ function emitValueFacts(db, filePath, root) {
2284
+ const emitter = { db, filePath, enclosing: null };
2285
+ emitNestedDefinitions(emitter, root);
2286
+ emitScopeWrites(emitter, readScope(root, []), true);
1880
2287
  emitExpressionFacts(emitter, root);
1881
2288
  }
1882
2289
 
@@ -1962,7 +2369,7 @@ function resolveModule(importingFile, spec, options) {
1962
2369
  }
1963
2370
 
1964
2371
  // src/values/evaluator.ts
1965
- var import_resolution2 = require("@suss/resolution");
2372
+ var import_resolution3 = require("@suss/resolution");
1966
2373
  var import_values3 = require("@suss/values");
1967
2374
 
1968
2375
  // src/values/lowering.ts
@@ -1989,7 +2396,7 @@ function pythonLowering(options) {
1989
2396
  return {
1990
2397
  idOf: (node) => node.id,
1991
2398
  expression: (node) => expressionOf(node, originOf2),
1992
- statement: statementOf,
2399
+ statement: statementOf2,
1993
2400
  siteOf,
1994
2401
  functionOf,
1995
2402
  writtenTo: (node) => context === null ? null : context.writtenTo(node),
@@ -2253,7 +2660,7 @@ var STATEMENT_TYPES = {
2253
2660
  body: blockStatements2(field(node, "body"))
2254
2661
  })
2255
2662
  };
2256
- function statementOf(node) {
2663
+ function statementOf2(node) {
2257
2664
  const lower = STATEMENT_TYPES[node.type];
2258
2665
  return lower === void 0 ? { kind: "opaque" } : lower(node);
2259
2666
  }
@@ -2750,7 +3157,7 @@ function contextOver(db, nodes) {
2750
3157
  return null;
2751
3158
  }
2752
3159
  const answer = writtenValueOf(db, key);
2753
- return answer === null ? null : (0, import_resolution2.nodeOfKey)(rootsByFile, answer);
3160
+ return answer === null ? null : (0, import_resolution3.nodeOfKey)(rootsByFile, answer);
2754
3161
  },
2755
3162
  callable: (call) => {
2756
3163
  const callee = field(call, "function");
@@ -2879,7 +3286,7 @@ function composedPrefixOf(index, construction, routerPath) {
2879
3286
  return { kind: "composed", value: composed.value + ownPrefix.value };
2880
3287
  }
2881
3288
  function composedMountPrefix(index, construction) {
2882
- const paths = (0, import_resolution3.mountPathsOf)(mountEdgesOf(index), construction.valueKey);
3289
+ const paths = (0, import_resolution4.mountPathsOf)(mountEdgesOf(index), construction.valueKey);
2883
3290
  if (paths === null) {
2884
3291
  return {
2885
3292
  kind: "abstain",
@@ -3302,9 +3709,13 @@ function constructionThroughFacts(name, scope, scan) {
3302
3709
  if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
3303
3710
  return null;
3304
3711
  }
3305
- const nameKey = `${scan.bound.file}#${name}`;
3306
- const constructionKey2 = subjectConstructions(facts, [nameKey]).get(
3307
- nameKey
3712
+ const nameKey2 = nameKeyIn(
3713
+ scan.bound.file,
3714
+ scope.kind === "function" ? scope.node : enclosingFunction(scope.node),
3715
+ name
3716
+ );
3717
+ const constructionKey2 = subjectConstructions(facts, [nameKey2]).get(
3718
+ nameKey2
3308
3719
  )?.constructionKey;
3309
3720
  return constructionKey2 === void 0 ? null : scan.index.byValueKey.get(constructionKey2) ?? null;
3310
3721
  }
@@ -3375,11 +3786,15 @@ var WALK_DESCENTS = {
3375
3786
  scan,
3376
3787
  visit
3377
3788
  );
3378
- }
3789
+ },
3790
+ if_statement: descendInPlace,
3791
+ try_statement: descendInPlace,
3792
+ while_statement: descendInPlace,
3793
+ with_statement: descendInPlace,
3794
+ match_statement: descendInPlace
3379
3795
  };
3380
- function nestedStatements(stmt) {
3381
- const body = field(stmt, "body");
3382
- return body === null ? [] : bodyStatements(body);
3796
+ function descendInPlace(stmt, position, scan, visit) {
3797
+ walkStatements(nestedStatements(stmt), position, scan, visit);
3383
3798
  }
3384
3799
  function walkStatements(statements, position, scan, visit) {
3385
3800
  for (const stmt of statements) {
@@ -3391,11 +3806,11 @@ function walkStatements(statements, position, scan, visit) {
3391
3806
  visit(stmt, position, scan);
3392
3807
  }
3393
3808
  }
3394
- function targetNames(node) {
3809
+ function targetNames2(node) {
3395
3810
  if (node.type === "identifier") {
3396
3811
  return [node.text];
3397
3812
  }
3398
- return bodyStatements(node).flatMap(targetNames);
3813
+ return bodyStatements(node).flatMap(targetNames2);
3399
3814
  }
3400
3815
  function sequenceElementNames(node) {
3401
3816
  if (node === null || node.type !== "list" && node.type !== "tuple") {
@@ -3432,7 +3847,7 @@ function loopBindingsOf(stmt, position, scan) {
3432
3847
  });
3433
3848
  return bindings;
3434
3849
  }
3435
- for (const name of targetNames(left)) {
3850
+ for (const name of targetNames2(left)) {
3436
3851
  bindings.set(name, { kind: "unenumerable", location });
3437
3852
  }
3438
3853
  return bindings;
@@ -4460,7 +4875,7 @@ function rawSqlEffects(calls, options) {
4460
4875
  binding: (0, import_ir_core.storageBinding)({
4461
4876
  recognition: `python-${pattern.module}`,
4462
4877
  storageSystem: pattern.storageSystem,
4463
- scope: pattern.module,
4878
+ scope: "default",
4464
4879
  container: access.table
4465
4880
  }),
4466
4881
  callee: field(call, "function")?.text ?? "",
@@ -4604,16 +5019,23 @@ function typeNameOf(annotation) {
4604
5019
  if (quoted !== null) {
4605
5020
  return quoted;
4606
5021
  }
5022
+ if (annotation.type === "binary_operator") {
5023
+ const named = [field(annotation, "left"), field(annotation, "right")].find(
5024
+ (side) => side !== null && side.type !== "none"
5025
+ );
5026
+ return named === void 0 || named === null ? null : typeNameOf(named);
5027
+ }
4607
5028
  if (annotation.type === "generic_type") {
4608
5029
  const outer = annotation.namedChildren[0];
4609
5030
  const first = genericTypeArgs(annotation)[0];
4610
5031
  if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
4611
5032
  return typeNameOf(first);
4612
5033
  }
5034
+ return outer?.type === "identifier" ? outer.text : null;
4613
5035
  }
4614
5036
  return null;
4615
5037
  }
4616
- function typeGivenBy(statement, name) {
5038
+ function typeGivenBy(statement, name, options) {
4617
5039
  if (statement.type === "assignment") {
4618
5040
  if (field(statement, "left")?.text !== name) {
4619
5041
  return null;
@@ -4623,8 +5045,7 @@ function typeGivenBy(statement, name) {
4623
5045
  return typeNameOf(annotation);
4624
5046
  }
4625
5047
  const right = field(statement, "right");
4626
- const callee = right?.type === "call" ? field(right, "function") : null;
4627
- return callee?.type === "identifier" ? callee.text : null;
5048
+ return right?.type === "call" ? builtType(right, options) : null;
4628
5049
  }
4629
5050
  if (statement.type === "as_pattern") {
4630
5051
  const alias = field(statement, "alias");
@@ -4634,7 +5055,34 @@ function typeGivenBy(statement, name) {
4634
5055
  }
4635
5056
  return null;
4636
5057
  }
4637
- function declaredTypeName(name, from) {
5058
+ function builtType(call, options) {
5059
+ const root = rootOf(call);
5060
+ const callee = field(root, "function");
5061
+ if (callee?.type === "identifier") {
5062
+ return isClassName(callee.text) ? callee.text : resolvedReturnType(options, callee);
5063
+ }
5064
+ const object = callee?.type === "attribute" ? field(callee, "object") : null;
5065
+ if (object?.type === "identifier" && isClassName(object.text)) {
5066
+ return object.text;
5067
+ }
5068
+ const args = field(root, "arguments");
5069
+ const first = (args === null ? [] : children(args))[0];
5070
+ if (first?.type === "identifier" && isClassName(first.text)) {
5071
+ return first.text;
5072
+ }
5073
+ return callee === null ? null : resolvedReturnType(options, callee);
5074
+ }
5075
+ function resolvedReturnType(options, callee) {
5076
+ const settled = settledCallee(options, callee);
5077
+ return settled === void 0 ? null : returnTypeName(options.definitionAt(settled));
5078
+ }
5079
+ function settledCallee(options, callee) {
5080
+ const key = readKey(options.filePath, callee, enclosingFunction(callee));
5081
+ resolveCalls(options.facts, [key]);
5082
+ const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
5083
+ return resolved.length === 1 ? resolved[0] : void 0;
5084
+ }
5085
+ function declaredTypeName(name, from, options) {
4638
5086
  const fn = enclosingFunction(from);
4639
5087
  if (fn === null) {
4640
5088
  return null;
@@ -4647,7 +5095,7 @@ function declaredTypeName(name, from) {
4647
5095
  }
4648
5096
  }
4649
5097
  const visit = (node) => {
4650
- const given = typeGivenBy(node, name);
5098
+ const given = typeGivenBy(node, name, options);
4651
5099
  if (given !== null) {
4652
5100
  return given;
4653
5101
  }
@@ -4676,16 +5124,27 @@ function typedReceiverPattern(options, chain) {
4676
5124
  if (receiver === null) {
4677
5125
  return void 0;
4678
5126
  }
4679
- const typeName = declaredTypeName(receiver, chain.root);
5127
+ const typeName = declaredTypeName(receiver, chain.root, options);
4680
5128
  if (typeName === null) {
4681
5129
  return void 0;
4682
5130
  }
4683
- const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${typeName}`);
4684
- if (from === void 0) {
4685
- return void 0;
5131
+ const typeKey = `${options.filePath}#${typeName}`;
5132
+ const imported = options.facts.facts("pyImportedName").find((row) => String(row[0]) === typeKey);
5133
+ const direct = imported === void 0 ? void 0 : queryTypePattern(options, String(imported[1]), String(imported[2]));
5134
+ if (direct !== void 0) {
5135
+ return direct;
4686
5136
  }
5137
+ for (const origin of originsOf(options.facts, typeKey)) {
5138
+ const pattern = queryTypePattern(options, origin.module, origin.name);
5139
+ if (pattern !== void 0) {
5140
+ return pattern;
5141
+ }
5142
+ }
5143
+ return void 0;
5144
+ }
5145
+ function queryTypePattern(options, module2, name) {
4687
5146
  return options.patterns.find(
4688
- (pattern) => String(from[1]) === pattern.module && pattern.queryTypes.includes(String(from[2]))
5147
+ (pattern) => module2 === pattern.module && pattern.queryTypes.includes(name)
4689
5148
  );
4690
5149
  }
4691
5150
  function fieldsOf(chain, valueMethods) {
@@ -4731,16 +5190,80 @@ function keywordNames(call) {
4731
5190
  }
4732
5191
  }
4733
5192
  }
4734
- return picked;
5193
+ return picked;
5194
+ }
5195
+ function selectorOf(chain, valueMethods) {
5196
+ return [
5197
+ ...new Set(
5198
+ laterCalls(chain).filter((call) => !valueMethods.includes(methodNameOf(call))).flatMap(keywordNames)
5199
+ )
5200
+ ];
5201
+ }
5202
+ var TABLE_METHODS = ["select_from", "join"];
5203
+ function modelOf(chain, options) {
5204
+ const fromRoot = modelArgument(chain.root, chain.root, options);
5205
+ if (fromRoot !== null) {
5206
+ return fromRoot;
5207
+ }
5208
+ for (const call of laterCalls(chain)) {
5209
+ if (TABLE_METHODS.includes(methodNameOf(call))) {
5210
+ const named = modelArgument(call, chain.root, options);
5211
+ if (named !== null) {
5212
+ return named;
5213
+ }
5214
+ }
5215
+ }
5216
+ return isClassName(chain.subject) ? chain.subject : null;
5217
+ }
5218
+ function modelArgument(call, from, options) {
5219
+ const args = field(call, "arguments");
5220
+ const first = (args === null ? [] : children(args)).find(
5221
+ (argument) => argument.type !== "keyword_argument"
5222
+ );
5223
+ return first === void 0 ? null : modelNamed(first, from, options);
5224
+ }
5225
+ function modelNamed(node, from, options) {
5226
+ if (node.type === "identifier") {
5227
+ if (isClassName(node.text)) {
5228
+ return node.text;
5229
+ }
5230
+ const declared = declaredTypeName(node.text, from, options);
5231
+ return declared === null ? null : classBehind(declared, options);
5232
+ }
5233
+ if (node.type === "attribute") {
5234
+ const property = field(node, "attribute");
5235
+ if (property !== null && isClassName(property.text)) {
5236
+ return property.text;
5237
+ }
5238
+ const object = field(node, "object");
5239
+ return object === null ? null : modelNamed(object, from, options);
5240
+ }
5241
+ if (node.type === "call") {
5242
+ const callee = field(node, "function");
5243
+ const built = callee === null ? null : modelNamed(callee, from, options);
5244
+ return built ?? modelArgument(node, from, options);
5245
+ }
5246
+ return null;
4735
5247
  }
4736
- function selectorOf(chain, valueMethods) {
4737
- return [
4738
- ...new Set(
4739
- laterCalls(chain).filter((call) => !valueMethods.includes(methodNameOf(call))).flatMap(keywordNames)
4740
- )
4741
- ];
5248
+ function classBehind(typeName, options) {
5249
+ const key = `${options.filePath}#${typeName}`;
5250
+ originsOf(options.facts, key);
5251
+ const reached = new Set(
5252
+ options.facts.facts("wantedComesTo").filter((row) => String(row[0]) === key).map((row) => String(row[1]))
5253
+ );
5254
+ const classes = new Set(
5255
+ options.facts.facts("objectValue").map((row) => String(row[0])).filter((value) => reached.has(value))
5256
+ );
5257
+ const exported = options.facts.facts("exportsAs").find((row) => classes.has(String(row[2])));
5258
+ if (exported !== void 0) {
5259
+ return String(exported[1]);
5260
+ }
5261
+ return isClassName(typeName) ? typeName : null;
4742
5262
  }
4743
- function effectFor(pattern, chain, operation) {
5263
+ function isClassName(name) {
5264
+ return /^[A-Z][A-Za-z0-9_]*$/.test(name) && name !== name.toUpperCase();
5265
+ }
5266
+ function effectFor(pattern, chain, operation, options) {
4744
5267
  const valueMethods = pattern.valueMethods ?? [];
4745
5268
  const picked = selectorOf(chain, valueMethods);
4746
5269
  return {
@@ -4748,8 +5271,8 @@ function effectFor(pattern, chain, operation) {
4748
5271
  binding: (0, import_ir_core2.storageBinding)({
4749
5272
  recognition: "python-storage",
4750
5273
  storageSystem: pattern.storageSystem,
4751
- scope: pattern.module,
4752
- container: chain.subject
5274
+ scope: "default",
5275
+ container: modelOf(chain, options)
4753
5276
  }),
4754
5277
  callee: chain.last.text,
4755
5278
  interaction: {
@@ -4766,23 +5289,14 @@ function fileOf(key) {
4766
5289
  return at === -1 ? key : key.slice(0, at);
4767
5290
  }
4768
5291
  function returnTypeName(node) {
4769
- if (node === void 0) {
4770
- return null;
4771
- }
4772
- const annotation = field(node, "return_type");
4773
- if (annotation === null) {
4774
- return null;
4775
- }
4776
- return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
5292
+ const annotation = node === void 0 ? null : field(node, "return_type");
5293
+ return annotation === null ? null : typeNameOf(annotation);
4777
5294
  }
4778
5295
  function storageCallees(calls, filePath, couldMatch) {
4779
5296
  return chainsIn(calls).filter((chain) => couldMatch.has(methodName(chain.root))).map((chain) => field(chain.root, "function")).filter((callee) => callee !== null).map((callee) => readKey(filePath, callee, enclosingFunction(callee)));
4780
5297
  }
4781
5298
  function resolvedMethodPattern(options, callee) {
4782
- const resolved = options.facts.facts("wantedResolves").filter(
4783
- (row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
4784
- ).map((row) => String(row[1]));
4785
- const settled = resolved.length === 1 ? resolved[0] : void 0;
5299
+ const settled = settledCallee(options, callee);
4786
5300
  if (settled === void 0) {
4787
5301
  return void 0;
4788
5302
  }
@@ -4809,14 +5323,9 @@ function matchedChains(calls, options) {
4809
5323
  if (chains.length === 0) {
4810
5324
  return [];
4811
5325
  }
4812
- const alreadyAsked = new Set(
4813
- options.facts.facts("wanted").map((row) => String(row[0]))
4814
- );
4815
5326
  resolveCalls(
4816
5327
  options.facts,
4817
- storageCallees(calls, options.filePath, options.couldMatch).filter(
4818
- (key) => !alreadyAsked.has(key)
4819
- )
5328
+ storageCallees(calls, options.filePath, options.couldMatch)
4820
5329
  );
4821
5330
  const matched = [];
4822
5331
  for (const chain of chains) {
@@ -4859,7 +5368,7 @@ function storageCallIds(calls, options) {
4859
5368
  }
4860
5369
  function storageEffects(calls, options) {
4861
5370
  return matchedChains(calls, options).flatMap(
4862
- ({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
5371
+ ({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation, options)]
4863
5372
  );
4864
5373
  }
4865
5374
 
@@ -5155,6 +5664,7 @@ function classRouteUnits(pattern, pack, classification, classNode, module2, opti
5155
5664
  pattern
5156
5665
  ),
5157
5666
  storage: options.storage,
5667
+ importedDefinition: options.importedDefinition,
5158
5668
  wrappers: wrappersAround(
5159
5669
  pattern,
5160
5670
  pack,
@@ -5484,7 +5994,10 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
5484
5994
  if (functionName === void 0) {
5485
5995
  return [];
5486
5996
  }
5487
- const ctx = createAnnotationContext(module2.scopeFor);
5997
+ const ctx = createAnnotationContext(
5998
+ module2.scopeFor,
5999
+ options.importedDefinition ?? null
6000
+ );
5488
6001
  return readRoutePaths(
5489
6002
  pattern,
5490
6003
  readPathArgument(classification, options),
@@ -5523,6 +6036,7 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
5523
6036
  ),
5524
6037
  definitionsCtx: ctx,
5525
6038
  storage: options.storage,
6039
+ importedDefinition: options.importedDefinition,
5526
6040
  wrappers: wrappersAround(
5527
6041
  pattern,
5528
6042
  pack,
@@ -5620,7 +6134,10 @@ function buildRouteUnit(options) {
5620
6134
  storage: storageLookup
5621
6135
  } = options;
5622
6136
  const template = (0, import_extractor5.valueToReadFurtherFrom)(routePath);
5623
- const ctx = definitionsCtx ?? createAnnotationContext(module2.scopeFor);
6137
+ const ctx = definitionsCtx ?? createAnnotationContext(
6138
+ module2.scopeFor,
6139
+ options.importedDefinition ?? null
6140
+ );
5624
6141
  const parameters = readParameters(
5625
6142
  definitionNode,
5626
6143
  enclosingScope,
@@ -5701,7 +6218,7 @@ function buildRouteUnit(options) {
5701
6218
  declaredContract: null,
5702
6219
  readings: [
5703
6220
  routePath,
5704
- ...unreadRoleReadings(parameters, rangeOf(definitionNode)),
6221
+ ...template === null ? unreadRoleReadings(parameters, rangeOf(definitionNode)) : [],
5705
6222
  ...responseShape.passedOver
5706
6223
  ],
5707
6224
  ...collectedDefinitions(ctx) !== null ? { definitions: collectedDefinitions(ctx) } : {},
@@ -5796,7 +6313,7 @@ function readParameter(param, scope, ctx, pathParamNames, requestBodyFromAnnotat
5796
6313
  }
5797
6314
  const { name, typeNode } = info;
5798
6315
  const shape = typeNode !== null ? annotationToShape(typeNode, scope, ctx) : null;
5799
- const role = isInjectedParameter(param, injectedCallees) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
6316
+ const role = isInjectedParameter(param, injectedCallees, scope, ctx) ? null : roleOf(name, shape, pathParamNames, requestBodyFromAnnotatedClass);
5800
6317
  return {
5801
6318
  name,
5802
6319
  position,
@@ -5830,7 +6347,7 @@ function calleeName2(call) {
5830
6347
  }
5831
6348
  return null;
5832
6349
  }
5833
- function isInjectedParameter(param, injectedCallees) {
6350
+ function isInjectedParameter(param, injectedCallees, scope, ctx) {
5834
6351
  if (injectedCallees.size === 0) {
5835
6352
  return false;
5836
6353
  }
@@ -5844,25 +6361,33 @@ function isInjectedParameter(param, injectedCallees) {
5844
6361
  if (callsAnInjector(field(param, "value"))) {
5845
6362
  return true;
5846
6363
  }
5847
- const containsInjectorCall = (node, depth) => {
6364
+ const followed = /* @__PURE__ */ new Set();
6365
+ const containsInjectorCall = (node, nodeScope, depth) => {
5848
6366
  if (node === null || depth > MAX_ANNOTATION_DEPTH) {
5849
6367
  return false;
5850
6368
  }
5851
6369
  if (callsAnInjector(node)) {
5852
6370
  return true;
5853
6371
  }
6372
+ if (node.type === "identifier" && !followed.has(node.id)) {
6373
+ followed.add(node.id);
6374
+ const alias = aliasValueOf(node.text, nodeScope, ctx);
6375
+ if (alias !== null && containsInjectorCall(alias.node, alias.scope, 0)) {
6376
+ return true;
6377
+ }
6378
+ }
5854
6379
  return node.namedChildren.some(
5855
- (child) => containsInjectorCall(child, depth + 1)
6380
+ (child) => containsInjectorCall(child, nodeScope, depth + 1)
5856
6381
  );
5857
6382
  };
5858
- return containsInjectorCall(field(param, "type"), 0);
6383
+ return containsInjectorCall(field(param, "type"), scope, 0);
5859
6384
  }
5860
6385
 
5861
6386
  // src/facts/why.ts
5862
6387
  var import_node_fs4 = __toESM(require("fs"), 1);
5863
6388
  var import_node_path4 = __toESM(require("path"), 1);
5864
- var import_datalog4 = require("@suss/datalog");
5865
- var import_resolution4 = require("@suss/resolution");
6389
+ var import_datalog3 = require("@suss/datalog");
6390
+ var import_resolution6 = require("@suss/resolution");
5866
6391
 
5867
6392
  // src/facts.ts
5868
6393
  function unitKey(filePath, range, name) {
@@ -5880,7 +6405,7 @@ function emitModuleImportFacts(db, filePath, module2, resolverOptions) {
5880
6405
  if (binding.kind !== "import" && binding.kind !== "importFrom") {
5881
6406
  continue;
5882
6407
  }
5883
- const nameKey = `${filePath}#${localName}`;
6408
+ const nameKey2 = `${filePath}#${localName}`;
5884
6409
  const moduleText = importedModuleText(
5885
6410
  binding.module,
5886
6411
  binding.relativeLevel
@@ -5896,16 +6421,16 @@ function emitModuleImportFacts(db, filePath, module2, resolverOptions) {
5896
6421
  resolution.status === "resolved" ? "resolved" : resolution.reason
5897
6422
  ]);
5898
6423
  const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
5899
- db.add("pyImportedName", [nameKey, moduleText, importedName]);
6424
+ db.add("pyImportedName", [nameKey2, moduleText, importedName]);
5900
6425
  if (scope.kind === "module") {
5901
- db.add("exportsAs", [filePath, localName, nameKey]);
6426
+ db.add("exportsAs", [filePath, localName, nameKey2]);
5902
6427
  }
5903
6428
  if (resolution.status === "resolved") {
5904
6429
  db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
5905
- db.add("imports", [nameKey, resolution.file, importedName]);
6430
+ db.add("imports", [nameKey2, resolution.file, importedName]);
5906
6431
  continue;
5907
6432
  }
5908
- db.add("imports", [nameKey, moduleText, importedName]);
6433
+ db.add("imports", [nameKey2, moduleText, importedName]);
5909
6434
  }
5910
6435
  }
5911
6436
  for (const openModule of module2.openImports) {
@@ -5973,7 +6498,7 @@ function parseWith(language, source) {
5973
6498
  var import_node_fs3 = __toESM(require("fs"), 1);
5974
6499
  var import_node_path3 = __toESM(require("path"), 1);
5975
6500
  var import_behavioral_ir5 = require("@suss/behavioral-ir");
5976
- var import_datalog3 = require("@suss/datalog");
6501
+ var import_datalog2 = require("@suss/datalog");
5977
6502
  var import_extractor10 = require("@suss/extractor");
5978
6503
 
5979
6504
  // src/diagnostics.ts
@@ -5991,271 +6516,300 @@ function buildPythonExtractionReport(args) {
5991
6516
  });
5992
6517
  }
5993
6518
 
6519
+ // src/importedDefinitions.ts
6520
+ function importedDefinitionLookup(db, files) {
6521
+ const fileOfModuleScope = /* @__PURE__ */ new Map();
6522
+ const byFile = /* @__PURE__ */ new Map();
6523
+ for (const file of files) {
6524
+ fileOfModuleScope.set(file.module.moduleScope.node.id, file.file);
6525
+ byFile.set(file.file, file);
6526
+ }
6527
+ const found = /* @__PURE__ */ new Map();
6528
+ return (scope, name) => {
6529
+ const fromFile = fileOfModuleScope.get(moduleScopeOf(scope).node.id);
6530
+ if (fromFile === void 0) {
6531
+ return null;
6532
+ }
6533
+ const key = `${fromFile}#${name}`;
6534
+ const known = found.get(key);
6535
+ if (known !== void 0) {
6536
+ return known;
6537
+ }
6538
+ const definition = definitionAmong(originsOf(db, key), byFile);
6539
+ found.set(key, definition);
6540
+ return definition;
6541
+ };
6542
+ }
6543
+ function definitionAmong(origins, byFile) {
6544
+ for (const origin of origins) {
6545
+ const file = byFile.get(origin.module);
6546
+ const binding = file?.module.moduleScope.bindings.get(origin.name);
6547
+ if (file === void 0 || binding === void 0) {
6548
+ continue;
6549
+ }
6550
+ const node = definedNodeOf(binding);
6551
+ if (node !== null) {
6552
+ return {
6553
+ node,
6554
+ moduleScope: file.module.moduleScope,
6555
+ scopeFor: file.module.scopeFor
6556
+ };
6557
+ }
6558
+ }
6559
+ return null;
6560
+ }
6561
+ function definedNodeOf(binding) {
6562
+ if (binding.kind === "classDef") {
6563
+ return binding.node;
6564
+ }
6565
+ if (binding.kind === "assignment") {
6566
+ return binding.value;
6567
+ }
6568
+ return null;
6569
+ }
6570
+
5994
6571
  // src/reach/closure.ts
5995
6572
  var import_behavioral_ir4 = require("@suss/behavioral-ir");
5996
- var import_datalog2 = require("@suss/datalog");
6573
+ var import_datalog = require("@suss/datalog");
5997
6574
  var import_extractor7 = require("@suss/extractor");
5998
6575
 
5999
6576
  // src/reach/resolveCallee.ts
6000
- var stop = (reason) => ({ kind: "stop", reason });
6001
- var MODULE_STOP = {
6002
- ambiguous: "multipleSources",
6003
- outsideRoots: "outsideRun"
6577
+ var import_resolution5 = require("@suss/resolution");
6578
+ var STOP_FOR = {
6579
+ severalSources: "multipleSources",
6580
+ outsideRun: "outsideRun",
6581
+ unsettled: "unsettledValue",
6582
+ undeclared: "noDeclaration"
6004
6583
  };
6005
- function resolveCallee2(call, site, ctx) {
6006
- const callee = field(call, "function");
6007
- if (callee === null) {
6008
- return { kind: "stopped", reason: "noDeclaration" };
6584
+ var NO_DECLARATION = {
6585
+ kind: "stopped",
6586
+ reason: "noDeclaration"
6587
+ };
6588
+ function calleeSpellings(calls, ctx) {
6589
+ const spellingOf = /* @__PURE__ */ new Map();
6590
+ const keys = /* @__PURE__ */ new Set();
6591
+ for (const { call, site } of calls) {
6592
+ const spelling = spellingFor(call, site, ctx);
6593
+ spellingOf.set(call.id, spelling);
6594
+ if (spelling.kind === "key") {
6595
+ keys.add(spelling.key);
6596
+ }
6009
6597
  }
6010
- return asCallee(resolveExpression(callee, site, ctx), ctx);
6598
+ return { spellingOf, outcomes: (0, import_resolution5.calleeOutcomes)(ctx.facts, [...keys]) };
6011
6599
  }
6012
- function resolveNamedFunctionArgument(name, site, ctx) {
6013
- if (site.rebound.has(name)) {
6014
- return null;
6600
+ function resolveCallee2(call, site, ctx, read) {
6601
+ const spelling = read?.spellingOf.get(call.id) ?? spellingFor(call, site, ctx);
6602
+ if (spelling.kind === "stopped") {
6603
+ return spelling;
6015
6604
  }
6016
- const binding = resolveName(site.scope, name);
6017
- if (binding !== null && binding.kind === "assignment") {
6605
+ const outcome = read?.outcomes.get(spelling.key) ?? (0, import_resolution5.calleeOutcomeOf)(ctx.facts, spelling.key);
6606
+ return asCallee(outcome, site.owner, ctx);
6607
+ }
6608
+ function functionNamed(nameKey2, ctx) {
6609
+ if (assignedElsewhere(ctx.facts, nameKey2)) {
6018
6610
  return null;
6019
6611
  }
6020
- const resolved = binding === null ? throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set()) : resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
6021
- return resolved.kind === "function" ? resolved.target : null;
6612
+ const outcome = (0, import_resolution5.calleeOutcomeOf)(ctx.facts, nameKey2);
6613
+ return outcome.kind === "function" ? functionAt(outcome.key, ctx) : null;
6022
6614
  }
6023
- function asCallee(resolved, ctx) {
6024
- if (resolved.kind === "function") {
6025
- return { kind: "followed", target: resolved.target };
6026
- }
6027
- if (resolved.kind === "class") {
6028
- return asCallee(
6029
- memberOfClass(resolved.file, resolved.node, "__init__", ctx),
6030
- ctx
6031
- );
6032
- }
6033
- if (resolved.kind === "stop") {
6034
- return { kind: "stopped", reason: resolved.reason };
6035
- }
6036
- if (resolved.kind === "package") {
6037
- return { kind: "stopped", reason: "outsideRun" };
6615
+ function assignedElsewhere(facts, nameKey2) {
6616
+ return (0, import_resolution5.writtenSourcesOf)(facts, nameKey2).some(
6617
+ (source) => !facts.has("func", [source])
6618
+ );
6619
+ }
6620
+ function readThrough(node) {
6621
+ if (node?.type !== "parenthesized_expression") {
6622
+ return node;
6038
6623
  }
6039
- return { kind: "stopped", reason: "noDeclaration" };
6624
+ return readThrough(children(node)[0] ?? null);
6040
6625
  }
6041
- var EXPRESSION_RESOLVERS = {
6042
- identifier: (node, site, ctx) => resolveIdentifier(node.text, site, ctx),
6043
- attribute: (node, site, ctx) => {
6044
- const object = field(node, "object");
6045
- const attribute = field(node, "attribute");
6046
- if (object === null || attribute === null) {
6047
- return stop("noDeclaration");
6048
- }
6049
- return memberOf(resolveExpression(object, site, ctx), attribute.text, ctx);
6050
- },
6051
- // `Service().run()` runs a method on whatever the constructor built.
6052
- call: (node, site, ctx) => {
6053
- const callee = field(node, "function");
6054
- if (callee === null) {
6055
- return stop("noDeclaration");
6056
- }
6057
- const built = resolveExpression(callee, site, ctx);
6058
- return built.kind === "class" ? built : stop("noDeclaration");
6059
- },
6060
- parenthesized_expression: (node, site, ctx) => {
6061
- const inner = node.namedChildren[0];
6062
- return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
6626
+ function spellingFor(call, site, ctx) {
6627
+ const callee = readThrough(field(call, "function"));
6628
+ if (callee === null) {
6629
+ return { kind: "stopped", reason: "noDeclaration" };
6063
6630
  }
6064
- };
6065
- function resolveExpression(node, site, ctx) {
6066
- const resolver = EXPRESSION_RESOLVERS[node.type];
6067
- return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
6631
+ return throughModules(callee, site, ctx) ?? {
6632
+ kind: "key",
6633
+ key: readKey(site.file.file, callee, enclosingFunction(callee))
6634
+ };
6068
6635
  }
6069
- function resolveIdentifier(name, site, ctx) {
6070
- if (site.rebound.has(name)) {
6071
- return stop("unsettledValue");
6636
+ function asCallee(outcome, owner, ctx) {
6637
+ if (outcome.kind === "function") {
6638
+ return functionCallee(outcome.key, ctx);
6072
6639
  }
6073
- if (isReceiver(name, site.scope)) {
6074
- return receiverClass(site);
6640
+ if (outcome.kind === "object") {
6641
+ return constructorOf(outcome.key, ctx);
6075
6642
  }
6076
- const binding = resolveName(site.scope, name);
6077
- if (binding === null) {
6078
- return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
6643
+ if (outcome.kind === "callerSupplied") {
6644
+ return outcome.key.startsWith(`${owner}#`) ? { kind: "stopped", reason: "callerSupplied" } : { kind: "stopped", reason: "unsettledValue" };
6079
6645
  }
6080
- return resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
6081
- }
6082
- function isReceiver(name, scope) {
6083
- return (name === "self" || name === "cls") && scope.kind === "function" && scope.parent?.kind === "class";
6646
+ return {
6647
+ kind: "stopped",
6648
+ reason: STOP_FOR[outcome.kind] ?? "noDeclaration"
6649
+ };
6084
6650
  }
6085
- function receiverClass(site) {
6086
- const classScope = site.scope.parent;
6087
- if (classScope === null || classScope === void 0) {
6088
- return stop("noDeclaration");
6651
+ function functionCallee(key, ctx) {
6652
+ if (ctx.definitions.get(key)?.type === "lambda") {
6653
+ return { kind: "stopped", reason: "unsettledValue" };
6089
6654
  }
6090
- return { kind: "class", file: site.file, node: classScope.node };
6091
- }
6092
- var BINDING_RESOLVERS = {
6093
- functionDef: (binding, file) => ({
6094
- kind: "function",
6095
- target: reachedFunction(file, binding.node)
6096
- }),
6097
- classDef: (binding, file) => ({ kind: "class", file, node: binding.node }),
6098
- parameter: () => stop("callerSupplied"),
6099
- // `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
6100
- import: (binding, file, _scope, ctx) => moduleNamed(
6101
- file,
6102
- binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
6103
- 0,
6104
- ctx
6105
- ),
6106
- importFrom: (binding, file, _scope, ctx, visited) => memberOf(
6107
- moduleNamed(file, binding.module, binding.relativeLevel, ctx),
6108
- binding.importedName,
6109
- ctx,
6110
- visited
6111
- ),
6112
- // `run = load` is an alias worth one hop; anything else is a value.
6113
- assignment: (binding, file, scope, ctx, visited) => aliasedValue(binding.value, file, scope, ctx, visited),
6114
- global: () => stop("unsettledValue"),
6115
- nonlocal: () => stop("unsettledValue")
6116
- };
6117
- function resolveBinding(binding, file, scope, ctx, visited) {
6118
- const resolver = BINDING_RESOLVERS[binding.kind];
6119
- return resolver(binding, file, scope, ctx, visited);
6655
+ const target = functionAt(key, ctx);
6656
+ return target === null ? NO_DECLARATION : { kind: "followed", target };
6120
6657
  }
6121
- function aliasedValue(value, file, scope, ctx, visited) {
6122
- if (value === null) {
6123
- return stop("unsettledValue");
6124
- }
6125
- if (value.type === "identifier") {
6126
- const binding = resolveName(scope, value.text);
6127
- if (binding === null || binding.kind === "assignment") {
6128
- return stop("unsettledValue");
6129
- }
6130
- return resolveBinding(binding, file, scope, ctx, visited);
6131
- }
6132
- if (value.type === "call") {
6133
- const callee = field(value, "function");
6134
- const built = callee === null ? stop("unsettledValue") : aliasedValue(callee, file, scope, ctx, visited);
6135
- return built.kind === "class" ? built : stop("unsettledValue");
6136
- }
6137
- if (value.type === "attribute") {
6138
- const object = field(value, "object");
6139
- const attribute = field(value, "attribute");
6140
- if (object === null || attribute === null) {
6141
- return stop("unsettledValue");
6142
- }
6143
- return memberOf(
6144
- aliasedValue(object, file, scope, ctx, visited),
6145
- attribute.text,
6146
- ctx
6147
- );
6658
+ function constructorOf(classKey, ctx) {
6659
+ const declared = ctx.facts.lookup("holdsProperty", 0, classKey).find((row) => String(row[1]) === "__init__");
6660
+ if (declared === void 0) {
6661
+ return NO_DECLARATION;
6148
6662
  }
6149
- return stop("unsettledValue");
6663
+ return functionCallee(String(declared[2]), ctx);
6150
6664
  }
6151
- function reachedFunction(file, node, owner) {
6665
+ function functionAt(key, ctx) {
6666
+ const node = ctx.definitions.get(key);
6667
+ const file = ctx.filesByPath.get(key.slice(0, key.lastIndexOf(":")));
6668
+ if (node === void 0 || file === void 0) {
6669
+ return null;
6670
+ }
6152
6671
  const name = field(node, "name")?.text ?? "<anon>";
6153
- const ownerName = owner === void 0 ? null : field(owner, "name")?.text;
6672
+ const owner = ownerClassOf(node);
6673
+ const ownerName = owner === null ? void 0 : field(owner, "name")?.text;
6154
6674
  return {
6155
6675
  file,
6156
6676
  node,
6157
6677
  name,
6158
- exportPath: ownerName === null || ownerName === void 0 ? [name] : [ownerName, name]
6678
+ exportPath: ownerName === void 0 ? [name] : [ownerName, name]
6159
6679
  };
6160
6680
  }
6161
- function moduleNamed(from, module2, relativeLevel, ctx) {
6162
- const resolution = resolveModule(
6163
- from.file,
6164
- { module: module2, relativeLevel },
6165
- { roots: ctx.roots }
6166
- );
6167
- if (resolution.status !== "resolved") {
6168
- return resolution.reason === "external" ? { kind: "package", dotted: module2, relativeLevel, from } : stop(MODULE_STOP[resolution.reason]);
6681
+ function ownerClassOf(node) {
6682
+ for (let up = node.parent; up !== null; up = up.parent) {
6683
+ if (isFunction(up)) {
6684
+ return null;
6685
+ }
6686
+ if (up.type === "class_definition") {
6687
+ return up;
6688
+ }
6169
6689
  }
6170
- const file = ctx.filesByPath.get(resolution.file);
6171
- return file === void 0 ? stop("outsideRun") : { kind: "module", file };
6690
+ return null;
6172
6691
  }
6173
- function memberOf(base, name, ctx, visited = /* @__PURE__ */ new Set()) {
6174
- if (base.kind === "module") {
6175
- return memberOfModule(base.file, name, ctx, visited);
6692
+ function throughModules(callee, site, ctx) {
6693
+ if (callee.type === "identifier") {
6694
+ return resolveName(site.scope, callee.text) === null ? openImportMember(site.file, callee.text, ctx, /* @__PURE__ */ new Set()) : null;
6176
6695
  }
6177
- if (base.kind === "package") {
6178
- return moduleNamed(
6179
- base.from,
6180
- base.dotted === "" ? name : `${base.dotted}.${name}`,
6181
- base.relativeLevel,
6182
- ctx
6183
- );
6696
+ if (callee.type !== "attribute") {
6697
+ return null;
6184
6698
  }
6185
- if (base.kind === "class") {
6186
- return memberOfClass(base.file, base.node, name, ctx);
6699
+ const object = field(callee, "object");
6700
+ const attribute = field(callee, "attribute");
6701
+ if (object === null || attribute === null) {
6702
+ return null;
6187
6703
  }
6188
- if (base.kind === "stop") {
6189
- return base;
6704
+ const base = moduleAt(object, site, ctx);
6705
+ return base === null ? null : memberKey(base, attribute.text, ctx);
6706
+ }
6707
+ function openImportMember(file, name, ctx, visited) {
6708
+ if (visited.has(file.file)) {
6709
+ return null;
6710
+ }
6711
+ visited.add(file.file);
6712
+ const found = /* @__PURE__ */ new Set();
6713
+ for (const spec of file.module.openImports) {
6714
+ const dots = spec.length - spec.replace(/^\.+/, "").length;
6715
+ const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
6716
+ if (opened === null || opened.kind !== "module") {
6717
+ continue;
6718
+ }
6719
+ const member = opened.file.module.moduleScope.bindings.has(name) ? { kind: "key", key: `${opened.file.file}#${name}` } : openImportMember(opened.file, name, ctx, visited);
6720
+ if (member?.kind === "key") {
6721
+ found.add(member.key);
6722
+ }
6190
6723
  }
6191
- return stop("noDeclaration");
6724
+ if (found.size > 1) {
6725
+ return { kind: "stopped", reason: "multipleSources" };
6726
+ }
6727
+ const only = [...found][0];
6728
+ return only === void 0 ? null : { kind: "key", key: only };
6192
6729
  }
6193
- function memberOfClass(file, classNode, name, ctx) {
6194
- const classScope = file.module.scopeFor.get(classNode.id);
6195
- const binding = classScope?.bindings.get(name);
6196
- if (binding === void 0) {
6197
- return stop("noDeclaration");
6730
+ function moduleAt(node, site, ctx) {
6731
+ if (node.type === "identifier") {
6732
+ return importedModule(node.text, site, ctx);
6198
6733
  }
6199
- if (binding.kind === "functionDef") {
6200
- return {
6201
- kind: "function",
6202
- target: reachedFunction(file, binding.node, classNode)
6203
- };
6734
+ if (node.type !== "attribute") {
6735
+ return null;
6204
6736
  }
6205
- return resolveBinding(binding, file, classScope, ctx, /* @__PURE__ */ new Set());
6737
+ const object = field(node, "object");
6738
+ const attribute = field(node, "attribute");
6739
+ if (object === null || attribute === null) {
6740
+ return null;
6741
+ }
6742
+ const base = moduleAt(object, site, ctx);
6743
+ return base === null ? null : submoduleOf(base, attribute.text, ctx);
6206
6744
  }
6207
- function memberOfModule(file, name, ctx, visited) {
6208
- const key = `${file.file}#${name}`;
6209
- if (visited.has(key)) {
6210
- return stop("unsettledValue");
6745
+ function importedModule(name, site, ctx) {
6746
+ const binding = resolveName(site.scope, name);
6747
+ if (binding?.kind === "import") {
6748
+ const head = binding.module.split(".")[0];
6749
+ const dotted = binding.localName === head ? binding.localName : binding.module;
6750
+ return moduleNamed(site.file, dotted, 0, ctx);
6211
6751
  }
6212
- visited.add(key);
6213
- const binding = file.module.moduleScope.bindings.get(name);
6214
- if (binding !== void 0) {
6215
- return resolveBinding(binding, file, file.module.moduleScope, ctx, visited);
6752
+ if (binding?.kind !== "importFrom") {
6753
+ return null;
6216
6754
  }
6217
- if (file.file.endsWith("__init__.py")) {
6218
- const submodule = moduleNamed(file, name, 1, ctx);
6219
- if (submodule.kind === "module") {
6220
- return submodule;
6221
- }
6755
+ const from = moduleNamed(
6756
+ site.file,
6757
+ binding.module,
6758
+ binding.relativeLevel,
6759
+ ctx
6760
+ );
6761
+ return from === null ? null : submoduleOf(from, binding.importedName, ctx);
6762
+ }
6763
+ function submoduleOf(base, name, ctx) {
6764
+ if (base.kind === "package") {
6765
+ return moduleNamed(
6766
+ base.from,
6767
+ base.dotted === "" ? name : `${base.dotted}.${name}`,
6768
+ base.relativeLevel,
6769
+ ctx
6770
+ );
6222
6771
  }
6223
- return throughOpenImports(file, name, ctx, visited);
6772
+ return base.file.file.endsWith("__init__.py") ? moduleNamed(base.file, name, 1, ctx) : null;
6224
6773
  }
6225
- function throughOpenImports(file, name, ctx, visited) {
6226
- const found = [];
6227
- for (const spec of file.module.openImports) {
6228
- const dots = spec.length - spec.replace(/^\.+/, "").length;
6229
- const opened = moduleNamed(file, spec.slice(dots), dots, ctx);
6230
- if (opened.kind !== "module") {
6231
- continue;
6232
- }
6233
- const member = memberOfModule(opened.file, name, ctx, visited);
6234
- if (member.kind !== "stop") {
6235
- found.push(member);
6236
- }
6774
+ function memberKey(base, name, ctx) {
6775
+ if (base.kind === "package") {
6776
+ return null;
6237
6777
  }
6238
- if (found.length > 1) {
6239
- return stop("multipleSources");
6778
+ if (base.file.module.moduleScope.bindings.has(name)) {
6779
+ return { kind: "key", key: `${base.file.file}#${name}` };
6780
+ }
6781
+ return openImportMember(base.file, name, ctx, /* @__PURE__ */ new Set());
6782
+ }
6783
+ function moduleNamed(from, module2, relativeLevel, ctx) {
6784
+ const resolution = resolveModule(
6785
+ from.file,
6786
+ { module: module2, relativeLevel },
6787
+ { roots: ctx.roots }
6788
+ );
6789
+ if (resolution.status !== "resolved") {
6790
+ return resolution.reason === "external" ? { kind: "package", dotted: module2, relativeLevel, from } : null;
6240
6791
  }
6241
- return found[0] ?? stop("noDeclaration");
6792
+ const file = ctx.filesByPath.get(resolution.file);
6793
+ return file === void 0 ? null : { kind: "module", file };
6242
6794
  }
6243
6795
 
6244
6796
  // src/reach/closure.ts
6245
6797
  var REACHABLE_RULES = [
6246
- (0, import_datalog2.rule)("reachable", [(0, import_datalog2.variable)("f")], [(0, import_datalog2.lit)("entry", (0, import_datalog2.variable)("f"))]),
6247
- (0, import_datalog2.rule)(
6798
+ (0, import_datalog.rule)("reachable", [(0, import_datalog.variable)("f")], [(0, import_datalog.lit)("entry", (0, import_datalog.variable)("f"))]),
6799
+ (0, import_datalog.rule)(
6248
6800
  "reachable",
6249
- [(0, import_datalog2.variable)("g")],
6250
- [(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"))]
6801
+ [(0, import_datalog.variable)("g")],
6802
+ [(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"))]
6251
6803
  )
6252
6804
  ];
6253
6805
  function reachedFunctions(seeds, options) {
6254
6806
  const ctx = {
6255
6807
  filesByPath: new Map(options.files.map((file) => [file.file, file])),
6256
- roots: options.roots
6808
+ roots: options.roots,
6809
+ facts: options.facts,
6810
+ definitions: options.definitions
6257
6811
  };
6258
- const db = new import_datalog2.Database();
6812
+ const db = new import_datalog.Database();
6259
6813
  const functionByKey = /* @__PURE__ */ new Map();
6260
6814
  const seedKeys = /* @__PURE__ */ new Set();
6261
6815
  const scanned = /* @__PURE__ */ new Set();
@@ -6275,21 +6829,26 @@ function reachedFunctions(seeds, options) {
6275
6829
  db.add("entry", [seed.key]);
6276
6830
  }
6277
6831
  for (; ; ) {
6278
- (0, import_datalog2.evaluate)(db, REACHABLE_RULES);
6832
+ (0, import_datalog.evaluate)(db, REACHABLE_RULES);
6279
6833
  const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
6280
6834
  if (frontier.length === 0) {
6281
6835
  break;
6282
6836
  }
6283
- for (const key of frontier) {
6837
+ const bodies = frontier.flatMap((key) => {
6284
6838
  scanned.add(key);
6285
6839
  const source = functionByKey.get(key);
6286
- if (source === void 0) {
6287
- continue;
6288
- }
6840
+ return source === void 0 ? [] : [{ key, source, ...bodyOf(source) }];
6841
+ });
6842
+ const spellings = calleeSpellings(
6843
+ bodies.flatMap((body) => body.written),
6844
+ ctx
6845
+ );
6846
+ for (const { key, source, body, written } of bodies) {
6289
6847
  const scan = scanBody(
6290
6848
  source,
6291
6849
  ctx,
6292
- recognizedCallIds(source.node, options.storageFor(source.file))
6850
+ recognizedCallIds(source.node, options.storageFor(source.file)),
6851
+ { body, written, spellings }
6293
6852
  );
6294
6853
  if (scan.stops.length > 0) {
6295
6854
  stopsByKey.set(key, scan.stops);
@@ -6355,7 +6914,18 @@ var EMPTY_SCAN = {
6355
6914
  parameterCalls: [],
6356
6915
  passedPositions: /* @__PURE__ */ new Set()
6357
6916
  };
6358
- function scanBody(source, ctx, recognized) {
6917
+ function bodyOf(source) {
6918
+ const { file, node } = source;
6919
+ const body = field(node, "body");
6920
+ if (body === null) {
6921
+ return { body, written: [] };
6922
+ }
6923
+ return {
6924
+ body,
6925
+ written: callsWritten(file, body, scopeAt2(file, node), keyOf(source))
6926
+ };
6927
+ }
6928
+ function scanBody(source, ctx, recognized, read) {
6359
6929
  const followed = [];
6360
6930
  const stops = [];
6361
6931
  const placements = new import_behavioral_ir4.TargetPlacements();
@@ -6364,12 +6934,12 @@ function scanBody(source, ctx, recognized) {
6364
6934
  const seen = /* @__PURE__ */ new Set();
6365
6935
  const parameterCallsSeen = /* @__PURE__ */ new Set();
6366
6936
  const { file, node } = source;
6367
- const body = field(node, "body");
6368
- if (body === null) {
6937
+ const { written, spellings } = read;
6938
+ if (read.body === null) {
6369
6939
  return EMPTY_SCAN;
6370
6940
  }
6371
6941
  const ownParameters = callParameterNames(node, source.exportPath.length > 1);
6372
- const recordPassedArgs = (call, callee, calleeKey, where) => {
6942
+ const recordPassedArgs = (call, callee, calleeKey) => {
6373
6943
  const args = field(call, "arguments");
6374
6944
  if (args === null) {
6375
6945
  return;
@@ -6378,9 +6948,8 @@ function scanBody(source, ctx, recognized) {
6378
6948
  if (arg === null || arg.type !== "identifier") {
6379
6949
  return;
6380
6950
  }
6381
- const resolved = resolveNamedFunctionArgument(
6382
- arg.text,
6383
- { file, scope: where.scope, rebound: where.rebound },
6951
+ const resolved = functionNamed(
6952
+ readKey(file.file, arg, enclosingFunction(arg)),
6384
6953
  ctx
6385
6954
  );
6386
6955
  if (resolved === null) {
@@ -6400,13 +6969,9 @@ function scanBody(source, ctx, recognized) {
6400
6969
  }
6401
6970
  });
6402
6971
  };
6403
- const record = (call, where) => {
6972
+ const record = (call, site) => {
6404
6973
  const callee = calleeText(call);
6405
- const outcome = resolveCallee2(
6406
- call,
6407
- { file, scope: where.scope, rebound: where.rebound },
6408
- ctx
6409
- );
6974
+ const outcome = resolveCallee2(call, site, ctx, spellings);
6410
6975
  const placed = outcome.kind === "followed" ? {
6411
6976
  file: outcome.target.file.displayPath,
6412
6977
  span: spanOf(outcome.target.node)
@@ -6415,8 +6980,7 @@ function scanBody(source, ctx, recognized) {
6415
6980
  recordPassedArgs(
6416
6981
  call,
6417
6982
  callee,
6418
- outcome.kind === "followed" ? keyOf(outcome.target) : null,
6419
- where
6983
+ outcome.kind === "followed" ? keyOf(outcome.target) : null
6420
6984
  );
6421
6985
  if (outcome.kind === "stopped") {
6422
6986
  const stopKey = `${outcome.reason}:${callee}`;
@@ -6439,32 +7003,9 @@ function scanBody(source, ctx, recognized) {
6439
7003
  followed.push(outcome.target);
6440
7004
  }
6441
7005
  };
6442
- (0, import_extractor7.walkDescendants)(
6443
- body,
6444
- { scope: scopeAt2(file, node), rebound: reboundNames(body) },
6445
- {
6446
- at: (child, where) => {
6447
- if (child.type === "call") {
6448
- record(child, where);
6449
- }
6450
- },
6451
- into: (child, where) => {
6452
- if (child.type === "function_definition") {
6453
- return import_extractor7.SKIP_CHILDREN;
6454
- }
6455
- if (child.type === "lambda") {
6456
- return {
6457
- scope: where.scope,
6458
- rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
6459
- };
6460
- }
6461
- return {
6462
- scope: file.module.scopeFor.get(child.id) ?? where.scope,
6463
- rebound: where.rebound
6464
- };
6465
- }
6466
- }
6467
- );
7006
+ for (const { call, site } of written) {
7007
+ record(call, site);
7008
+ }
6468
7009
  return {
6469
7010
  followed,
6470
7011
  stops,
@@ -6474,6 +7015,18 @@ function scanBody(source, ctx, recognized) {
6474
7015
  passedPositions
6475
7016
  };
6476
7017
  }
7018
+ function callsWritten(file, body, outer, owner) {
7019
+ const found = [];
7020
+ (0, import_extractor7.walkDescendants)(body, outer, {
7021
+ at: (child, scope) => {
7022
+ if (child.type === "call") {
7023
+ found.push({ call: child, site: { file, scope, owner } });
7024
+ }
7025
+ },
7026
+ into: (child, scope) => child.type === "function_definition" ? import_extractor7.SKIP_CHILDREN : file.module.scopeFor.get(child.id) ?? scope
7027
+ });
7028
+ return found;
7029
+ }
6477
7030
  function scopeAt2(file, node) {
6478
7031
  let current = node;
6479
7032
  while (current !== null) {
@@ -6485,40 +7038,6 @@ function scopeAt2(file, node) {
6485
7038
  }
6486
7039
  return file.module.moduleScope;
6487
7040
  }
6488
- var REBINDING_FIELDS = {
6489
- assignment: "left",
6490
- augmented_assignment: "left",
6491
- named_expression: "name",
6492
- for_statement: "left",
6493
- for_in_clause: "left",
6494
- as_pattern: "alias"
6495
- };
6496
- function reboundNames(body) {
6497
- const names = /* @__PURE__ */ new Set();
6498
- (0, import_extractor7.walkDescendants)(body, null, {
6499
- at: (node) => {
6500
- const target = REBINDING_FIELDS[node.type];
6501
- if (target === void 0 || boundByBinder(node, body)) {
6502
- return;
6503
- }
6504
- const written = field(node, target);
6505
- if (written !== null) {
6506
- for (const name of identifiersUnder(written)) {
6507
- names.add(name);
6508
- }
6509
- }
6510
- },
6511
- into: (node) => node.type === "function_definition" ? import_extractor7.SKIP_CHILDREN : null
6512
- });
6513
- return names;
6514
- }
6515
- function boundByBinder(node, body) {
6516
- if (node.type !== "assignment") {
6517
- return false;
6518
- }
6519
- const statement = node.parent;
6520
- return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
6521
- }
6522
7041
  function identifiersUnder(node, found = []) {
6523
7042
  if (node.type === "identifier") {
6524
7043
  found.push(node.text);
@@ -6531,10 +7050,6 @@ function identifiersUnder(node, found = []) {
6531
7050
  }
6532
7051
  return found;
6533
7052
  }
6534
- function lambdaParameters(lambda) {
6535
- const parameters = field(lambda, "parameters");
6536
- return parameters === null ? [] : identifiersUnder(parameters);
6537
- }
6538
7053
  function libraryUnit(target, options) {
6539
7054
  const { file, node, name, exportPath } = target;
6540
7055
  const body = field(node, "body");
@@ -6685,6 +7200,18 @@ var PythonWrapperIndex = class {
6685
7200
  ownRoutes = /* @__PURE__ */ new Map();
6686
7201
  unitsByKey = /* @__PURE__ */ new Map();
6687
7202
  unitsByFile = /* @__PURE__ */ new Map();
7203
+ /** The project function a module-level name in this file refers to, or null for anything else. */
7204
+ functionCalled(file, name) {
7205
+ if (this.options.facts === void 0) {
7206
+ return null;
7207
+ }
7208
+ return functionNamed(nameKeyIn(file, null, name), {
7209
+ filesByPath: this.filesByPath,
7210
+ roots: this.options.roots,
7211
+ facts: this.options.facts,
7212
+ definitions: this.options.definitions
7213
+ });
7214
+ }
6688
7215
  register(covers, target, declared) {
6689
7216
  const registered = this.registered(target, declared);
6690
7217
  const table = covers.kind === "everyRoute" ? this.everyRoute : this.ownRoutes;
@@ -6731,20 +7258,12 @@ var PythonWrapperIndex = class {
6731
7258
  if (declared.form.type !== "dependency") {
6732
7259
  continue;
6733
7260
  }
6734
- const site = {
6735
- file,
6736
- scope: query.module.moduleScope,
6737
- rebound: /* @__PURE__ */ new Set()
6738
- };
6739
7261
  const targets = [
6740
7262
  ...decoratorDependencies(query.classification, declared.form),
6741
7263
  ...parameterDependencies(query.definitionNode, declared.form)
6742
7264
  ];
6743
7265
  for (const name of targets) {
6744
- const target = resolveNamedFunctionArgument(name, site, {
6745
- filesByPath: this.filesByPath,
6746
- roots: this.options.roots
6747
- });
7266
+ const target = this.functionCalled(file.file, name);
6748
7267
  if (target !== null) {
6749
7268
  found.push(this.registered(target, declared));
6750
7269
  }
@@ -6886,12 +7405,8 @@ function registerConstructorDependencies(file, declared, form, index) {
6886
7405
  if (listed === void 0) {
6887
7406
  continue;
6888
7407
  }
6889
- const site = { file, scope, rebound: /* @__PURE__ */ new Set() };
6890
7408
  for (const name of dependencyNamesIn(listed.node, form)) {
6891
- const target = resolveNamedFunctionArgument(name, site, {
6892
- filesByPath: index.filesByPath,
6893
- roots: index.options.roots
6894
- });
7409
+ const target = index.functionCalled(file.file, name);
6895
7410
  if (target !== null) {
6896
7411
  index.register(
6897
7412
  coverageOf(match, file, declared.pack),
@@ -7153,9 +7668,9 @@ async function extractPythonProject(options) {
7153
7668
  options.onCacheDiagnostic?.(lookup.diagnostic);
7154
7669
  if (lookup.kind === "hit") {
7155
7670
  options.onTiming?.(timer.report());
7156
- return { summaries: lookup.summaries, facts: new import_datalog3.Database() };
7671
+ return { summaries: lookup.summaries, facts: new import_datalog2.Database() };
7157
7672
  }
7158
- const db = new import_datalog3.Database();
7673
+ const db = new import_datalog2.Database();
7159
7674
  const summaries = [];
7160
7675
  const gapHandling = options.gapHandling ?? "permissive";
7161
7676
  const tallies = (0, import_extractor6.createPackTallies)(options.packs);
@@ -7219,9 +7734,11 @@ async function extractPythonProject(options) {
7219
7734
  packs: options.packs,
7220
7735
  roots: options.roots,
7221
7736
  facts: needsValues ? db : void 0,
7737
+ definitions,
7222
7738
  storageFor
7223
7739
  })
7224
7740
  );
7741
+ const importedDefinition = importedDefinitionLookup(db, bound);
7225
7742
  const discovered = /* @__PURE__ */ new Map();
7226
7743
  for (const boundFile of bound) {
7227
7744
  const { file, root, module: moduleBinding } = boundFile;
@@ -7235,6 +7752,7 @@ async function extractPythonProject(options) {
7235
7752
  routerIndex,
7236
7753
  gapHandling,
7237
7754
  wrappers: wrapperIndex,
7755
+ importedDefinition,
7238
7756
  ...needsValues ? { facts: db } : {},
7239
7757
  ...storage === void 0 ? {} : { storage }
7240
7758
  })
@@ -7300,7 +7818,9 @@ async function extractPythonProject(options) {
7300
7818
  files: bound,
7301
7819
  roots: options.roots,
7302
7820
  gapHandling,
7303
- storageFor
7821
+ storageFor,
7822
+ facts: db,
7823
+ definitions
7304
7824
  })
7305
7825
  );
7306
7826
  for (const [key, owners] of summariesBySeed) {
@@ -7491,7 +8011,7 @@ function width(node) {
7491
8011
  }
7492
8012
  var PythonWhySession = class {
7493
8013
  root;
7494
- db = new import_datalog4.Database();
8014
+ db = new import_datalog3.Database();
7495
8015
  locations = /* @__PURE__ */ new Map();
7496
8016
  trees = /* @__PURE__ */ new Map();
7497
8017
  constructor(options) {
@@ -7560,9 +8080,9 @@ var PythonWhySession = class {
7560
8080
  * which the caller says in its own words.
7561
8081
  */
7562
8082
  explain(value, options = {}) {
7563
- return (0, import_resolution4.explainResolvedKey)({
8083
+ return (0, import_resolution6.explainResolvedKey)({
7564
8084
  db: this.db,
7565
- rules: import_resolution4.RESOLUTION_RULES,
8085
+ rules: import_resolution6.RESOLUTION_RULES,
7566
8086
  key: readKey(value.file, value.node, enclosingFunction(value.node)),
7567
8087
  locate: (key) => this.locate(key),
7568
8088
  displayPath: (key) => this.displayPath(key),
@@ -7693,6 +8213,7 @@ async function pythonImportEvidence(options) {
7693
8213
  emitValueFacts,
7694
8214
  extractPythonProject,
7695
8215
  findPythonFiles,
8216
+ nameKeyIn,
7696
8217
  nodeId,
7697
8218
  objectReturnedBy,
7698
8219
  parsePython,