@suss/adapter-python 0.22.2 → 0.22.3
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/README.md +73 -16
- package/dist/index.cjs +1465 -1096
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +1488 -1119
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -34,6 +34,40 @@ function enclosingFunction(node) {
|
|
|
34
34
|
}
|
|
35
35
|
return null;
|
|
36
36
|
}
|
|
37
|
+
function stringContentValue(content) {
|
|
38
|
+
const base = content.startIndex;
|
|
39
|
+
let text2 = "";
|
|
40
|
+
let cursor = 0;
|
|
41
|
+
for (const child of children(content)) {
|
|
42
|
+
if (child.type !== "escape_interpolation") {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
text2 += content.text.slice(cursor, child.startIndex - base);
|
|
46
|
+
text2 += child.text.slice(0, 1);
|
|
47
|
+
cursor = child.endIndex - base;
|
|
48
|
+
}
|
|
49
|
+
return text2 + content.text.slice(cursor);
|
|
50
|
+
}
|
|
51
|
+
function parameterNameAndType(param) {
|
|
52
|
+
if (param.type === "identifier") {
|
|
53
|
+
return { name: param.text, typeNode: null };
|
|
54
|
+
}
|
|
55
|
+
if (param.type === "typed_parameter") {
|
|
56
|
+
const inner = param.namedChildren.find(
|
|
57
|
+
(child) => child !== null && child.type === "identifier"
|
|
58
|
+
);
|
|
59
|
+
return inner !== void 0 ? { name: inner.text, typeNode: field(param, "type") } : null;
|
|
60
|
+
}
|
|
61
|
+
if (param.type === "default_parameter") {
|
|
62
|
+
const nameNode = field(param, "name");
|
|
63
|
+
return nameNode?.type === "identifier" ? { name: nameNode.text, typeNode: null } : null;
|
|
64
|
+
}
|
|
65
|
+
if (param.type === "typed_default_parameter") {
|
|
66
|
+
const nameNode = field(param, "name");
|
|
67
|
+
return nameNode !== null ? { name: nameNode.text, typeNode: field(param, "type") } : null;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
37
71
|
function stringLiteralValue(node) {
|
|
38
72
|
if (node.type !== "string") {
|
|
39
73
|
return null;
|
|
@@ -44,7 +78,7 @@ function stringLiteralValue(node) {
|
|
|
44
78
|
continue;
|
|
45
79
|
}
|
|
46
80
|
if (child.type === "string_content") {
|
|
47
|
-
content += child
|
|
81
|
+
content += stringContentValue(child);
|
|
48
82
|
continue;
|
|
49
83
|
}
|
|
50
84
|
if (child.type === "interpolation") {
|
|
@@ -72,6 +106,8 @@ function stripDecorators(node) {
|
|
|
72
106
|
);
|
|
73
107
|
return { definition, decorators };
|
|
74
108
|
}
|
|
109
|
+
var NodeSet = class extends IdSet {
|
|
110
|
+
};
|
|
75
111
|
var NodeMap = class extends IdMap {
|
|
76
112
|
};
|
|
77
113
|
|
|
@@ -1418,14 +1454,14 @@ var EXIT_KINDS = {
|
|
|
1418
1454
|
continue_statement: "continue"
|
|
1419
1455
|
};
|
|
1420
1456
|
var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
1421
|
-
function exitKindOf(node) {
|
|
1457
|
+
function exitKindOf(node, thrown) {
|
|
1422
1458
|
let sawReturn = false;
|
|
1423
1459
|
const raises = (current) => {
|
|
1424
1460
|
for (const child of current.namedChildren) {
|
|
1425
1461
|
if (child === null || NESTED_FUNCTION_TYPES.has(child.type)) {
|
|
1426
1462
|
continue;
|
|
1427
1463
|
}
|
|
1428
|
-
if (child.type === "raise_statement") {
|
|
1464
|
+
if (child.type === "raise_statement" || thrown.has(child)) {
|
|
1429
1465
|
return true;
|
|
1430
1466
|
}
|
|
1431
1467
|
if (child.type === "return_statement") {
|
|
@@ -1459,10 +1495,12 @@ function childBlock(node) {
|
|
|
1459
1495
|
var Lowerer = class {
|
|
1460
1496
|
// tree-sitter hands back a fresh wrapper object each time a child is
|
|
1461
1497
|
// read, so a terminal is matched by its stable node id.
|
|
1462
|
-
constructor(terminals) {
|
|
1498
|
+
constructor(terminals, thrown) {
|
|
1463
1499
|
this.terminals = terminals;
|
|
1500
|
+
this.thrown = thrown;
|
|
1464
1501
|
}
|
|
1465
1502
|
terminals;
|
|
1503
|
+
thrown;
|
|
1466
1504
|
terminalsByStmt = /* @__PURE__ */ new Map();
|
|
1467
1505
|
terminalHome = new NodeMap();
|
|
1468
1506
|
attach(statement, candidates) {
|
|
@@ -1495,7 +1533,7 @@ var Lowerer = class {
|
|
|
1495
1533
|
condition: handleOf(field(clause, "condition")),
|
|
1496
1534
|
thenBody: this.lowerBlock(field(clause, "consequence")),
|
|
1497
1535
|
elseBody: this.lowerIfTail(clauses, index + 1),
|
|
1498
|
-
exitKind: exitKindOf(clause)
|
|
1536
|
+
exitKind: exitKindOf(clause, this.thrown)
|
|
1499
1537
|
};
|
|
1500
1538
|
this.attach(nested, [field(clause, "condition")]);
|
|
1501
1539
|
return [nested];
|
|
@@ -1509,7 +1547,7 @@ var Lowerer = class {
|
|
|
1509
1547
|
condition: handleOf(field(node, "condition")),
|
|
1510
1548
|
thenBody: this.lowerBlock(field(node, "consequence")),
|
|
1511
1549
|
elseBody: this.lowerIfTail(clauses, 0),
|
|
1512
|
-
exitKind: exitKindOf(node)
|
|
1550
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1513
1551
|
};
|
|
1514
1552
|
}
|
|
1515
1553
|
lowerTry(node) {
|
|
@@ -1524,7 +1562,7 @@ var Lowerer = class {
|
|
|
1524
1562
|
tryBody: this.lowerBlock(field(node, "body")),
|
|
1525
1563
|
catchBody: excepts.length > 0 ? excepts.flatMap((clause) => this.lowerBlock(childBlock(clause))) : null,
|
|
1526
1564
|
finallyBody: finallyClause === null ? null : this.lowerBlock(childBlock(finallyClause)),
|
|
1527
|
-
exitKind: exitKindOf(node)
|
|
1565
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1528
1566
|
};
|
|
1529
1567
|
}
|
|
1530
1568
|
lowerMatch(node) {
|
|
@@ -1541,15 +1579,21 @@ var Lowerer = class {
|
|
|
1541
1579
|
body: this.lowerBlock(field(clause, "consequence"))
|
|
1542
1580
|
};
|
|
1543
1581
|
});
|
|
1544
|
-
return { kind: "switch", groups, exitKind: exitKindOf(node) };
|
|
1582
|
+
return { kind: "switch", groups, exitKind: exitKindOf(node, this.thrown) };
|
|
1545
1583
|
}
|
|
1546
1584
|
lower(node) {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
return this.attach({ kind: "exit", exit, exitKind: exitKindOf(node) }, [
|
|
1585
|
+
if (this.thrown.has(node)) {
|
|
1586
|
+
return this.attach({ kind: "exit", exit: "throw", exitKind: "throw" }, [
|
|
1550
1587
|
node
|
|
1551
1588
|
]);
|
|
1552
1589
|
}
|
|
1590
|
+
const exit = EXIT_KINDS[node.type];
|
|
1591
|
+
if (exit !== void 0) {
|
|
1592
|
+
return this.attach(
|
|
1593
|
+
{ kind: "exit", exit, exitKind: exitKindOf(node, this.thrown) },
|
|
1594
|
+
[node]
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1553
1597
|
if (node.type === "if_statement") {
|
|
1554
1598
|
return this.attach(this.lowerIf(node), [field(node, "condition")]);
|
|
1555
1599
|
}
|
|
@@ -1560,7 +1604,7 @@ var Lowerer = class {
|
|
|
1560
1604
|
kind: "loop",
|
|
1561
1605
|
condition: handleOf(header),
|
|
1562
1606
|
body: this.lowerBlock(field(node, "body")),
|
|
1563
|
-
exitKind: exitKindOf(node)
|
|
1607
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1564
1608
|
},
|
|
1565
1609
|
[header]
|
|
1566
1610
|
);
|
|
@@ -1571,12 +1615,16 @@ var Lowerer = class {
|
|
|
1571
1615
|
if (node.type === "match_statement") {
|
|
1572
1616
|
return this.attach(this.lowerMatch(node), [field(node, "subject")]);
|
|
1573
1617
|
}
|
|
1574
|
-
return this.attach(
|
|
1618
|
+
return this.attach(
|
|
1619
|
+
{ kind: "opaque", exitKind: exitKindOf(node, this.thrown) },
|
|
1620
|
+
[node]
|
|
1621
|
+
);
|
|
1575
1622
|
}
|
|
1576
1623
|
};
|
|
1577
|
-
function lowerPythonBody(body, terminals) {
|
|
1624
|
+
function lowerPythonBody(body, terminals, thrown = new NodeSet()) {
|
|
1578
1625
|
const lowerer = new Lowerer(
|
|
1579
|
-
new Map(terminals.map((terminal) => [terminal.id, terminal]))
|
|
1626
|
+
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
1627
|
+
thrown
|
|
1580
1628
|
);
|
|
1581
1629
|
return {
|
|
1582
1630
|
statements: lowerer.lowerBlock(body),
|
|
@@ -1773,1192 +1821,1464 @@ function invocationEffects(definitionNode) {
|
|
|
1773
1821
|
});
|
|
1774
1822
|
}
|
|
1775
1823
|
|
|
1776
|
-
// src/paths/
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1824
|
+
// src/paths/raisedResponses.ts
|
|
1825
|
+
import { constantOf } from "@suss/values";
|
|
1826
|
+
|
|
1827
|
+
// src/values/evaluator.ts
|
|
1828
|
+
import { nodeOfKey } from "@suss/resolution";
|
|
1829
|
+
import { Evaluator, force, literalOf as literalOf2 } from "@suss/values";
|
|
1830
|
+
|
|
1831
|
+
// src/values/lowering.ts
|
|
1832
|
+
var ROOT_TYPES = /* @__PURE__ */ new Set(["module", "function_definition", "lambda"]);
|
|
1833
|
+
var LITERAL_TYPES = {
|
|
1834
|
+
integer: (node) => ({ kind: "literal", value: Number(node.text) }),
|
|
1835
|
+
float: (node) => ({ kind: "literal", value: Number(node.text) }),
|
|
1836
|
+
true: () => ({ kind: "literal", value: true }),
|
|
1837
|
+
false: () => ({ kind: "literal", value: false }),
|
|
1838
|
+
none: () => ({ kind: "literal", value: null })
|
|
1782
1839
|
};
|
|
1783
|
-
|
|
1784
|
-
|
|
1840
|
+
var WRITING_ROWS = /* @__PURE__ */ new Set(["append", "extend"]);
|
|
1841
|
+
function readingMethodsOf(rows) {
|
|
1842
|
+
return new Set(
|
|
1843
|
+
rows.flatMap(
|
|
1844
|
+
(row) => row.kind === "method" && !WRITING_ROWS.has(row.method) ? [row.method] : []
|
|
1845
|
+
)
|
|
1846
|
+
);
|
|
1785
1847
|
}
|
|
1786
|
-
function
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1848
|
+
function pythonLowering(options) {
|
|
1849
|
+
const { context, originOf: originOf2, rows } = options;
|
|
1850
|
+
const readingMethods = readingMethodsOf(rows);
|
|
1851
|
+
const mutatedByRoot = /* @__PURE__ */ new Map();
|
|
1852
|
+
return {
|
|
1853
|
+
idOf: (node) => node.id,
|
|
1854
|
+
expression: (node) => expressionOf(node, originOf2),
|
|
1855
|
+
statement: statementOf,
|
|
1856
|
+
siteOf,
|
|
1857
|
+
functionOf,
|
|
1858
|
+
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1859
|
+
callable: (node) => {
|
|
1860
|
+
if (context === null || hasDictionarySplat(node)) {
|
|
1861
|
+
return null;
|
|
1862
|
+
}
|
|
1863
|
+
return context.callable(node);
|
|
1864
|
+
},
|
|
1865
|
+
mutatedInNestedFunction: (root, name) => {
|
|
1866
|
+
let mutated = mutatedByRoot.get(root.id);
|
|
1867
|
+
if (mutated === void 0) {
|
|
1868
|
+
mutated = namesMutatedInNestedFunctions(root, readingMethods);
|
|
1869
|
+
mutatedByRoot.set(root.id, mutated);
|
|
1870
|
+
}
|
|
1871
|
+
return mutated.has(name);
|
|
1872
|
+
},
|
|
1873
|
+
freeNamesOf,
|
|
1874
|
+
holeNameOf: placeholderName,
|
|
1875
|
+
rows
|
|
1876
|
+
};
|
|
1803
1877
|
}
|
|
1804
|
-
function
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
const scalar = SCALARS[node.type];
|
|
1809
|
-
if (scalar !== void 0) {
|
|
1810
|
-
return scalar;
|
|
1878
|
+
function placeholderName(node) {
|
|
1879
|
+
const peeled = peelValue(node);
|
|
1880
|
+
if (peeled.type === "identifier") {
|
|
1881
|
+
return peeled.text;
|
|
1811
1882
|
}
|
|
1812
|
-
if (
|
|
1813
|
-
return
|
|
1883
|
+
if (peeled.type === "attribute") {
|
|
1884
|
+
return field(peeled, "attribute")?.text ?? "param";
|
|
1814
1885
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1886
|
+
return "param";
|
|
1887
|
+
}
|
|
1888
|
+
function peelValue(node) {
|
|
1889
|
+
const wraps = node.type === "parenthesized_expression" || node.type === "await";
|
|
1890
|
+
const inner = wraps ? children(node)[0] : void 0;
|
|
1891
|
+
return inner === void 0 ? node : peelValue(inner);
|
|
1892
|
+
}
|
|
1893
|
+
var OPAQUE = { kind: "opaque" };
|
|
1894
|
+
function fromFields(node, names, fallback2, build) {
|
|
1895
|
+
const found = names.map((name) => field(node, name));
|
|
1896
|
+
if (found.some((part) => part === null)) {
|
|
1897
|
+
return fallback2;
|
|
1818
1898
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1899
|
+
return build(...found);
|
|
1900
|
+
}
|
|
1901
|
+
function expressionOf(node, originOf2) {
|
|
1902
|
+
const peeled = peelValue(node);
|
|
1903
|
+
const literal = LITERAL_TYPES[peeled.type];
|
|
1904
|
+
if (literal !== void 0) {
|
|
1905
|
+
return literal(peeled);
|
|
1821
1906
|
}
|
|
1822
|
-
if (
|
|
1823
|
-
return
|
|
1907
|
+
if (peeled.type === "string") {
|
|
1908
|
+
return stringExpression(peeled);
|
|
1824
1909
|
}
|
|
1825
|
-
if (
|
|
1826
|
-
const first = children2(node)[0];
|
|
1910
|
+
if (peeled.type === "concatenated_string") {
|
|
1827
1911
|
return {
|
|
1828
|
-
|
|
1829
|
-
|
|
1912
|
+
kind: "template",
|
|
1913
|
+
parts: children(peeled).map((part) => ({ expression: part }))
|
|
1830
1914
|
};
|
|
1831
1915
|
}
|
|
1832
|
-
if (
|
|
1833
|
-
return {
|
|
1834
|
-
}
|
|
1835
|
-
return { type: "unknown" };
|
|
1836
|
-
}
|
|
1837
|
-
function returnedBodyShape(statement) {
|
|
1838
|
-
const returned = statement.namedChildren[0];
|
|
1839
|
-
if (returned == null) {
|
|
1840
|
-
return null;
|
|
1841
|
-
}
|
|
1842
|
-
const value = returned.type === "expression_list" ? children2(returned)[0] : returned;
|
|
1843
|
-
if (value === void 0) {
|
|
1844
|
-
return null;
|
|
1916
|
+
if (peeled.type === "identifier") {
|
|
1917
|
+
return { kind: "name", text: peeled.text };
|
|
1845
1918
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1919
|
+
if (peeled.type === "attribute") {
|
|
1920
|
+
return fromFields(
|
|
1921
|
+
peeled,
|
|
1922
|
+
["object", "attribute"],
|
|
1923
|
+
OPAQUE,
|
|
1924
|
+
(object, name) => ({
|
|
1925
|
+
kind: "member",
|
|
1926
|
+
object,
|
|
1927
|
+
name: name.text
|
|
1928
|
+
})
|
|
1929
|
+
);
|
|
1856
1930
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
const
|
|
1860
|
-
if (
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
const statement = statementIn(call);
|
|
1864
|
-
if (statement === null) {
|
|
1865
|
-
continue;
|
|
1866
|
-
}
|
|
1867
|
-
for (const access of readSqlAccess(statement, {
|
|
1868
|
-
dialect: pattern.storageSystem
|
|
1869
|
-
})) {
|
|
1870
|
-
effects.push({
|
|
1871
|
-
type: "interaction",
|
|
1872
|
-
binding: storageBinding({
|
|
1873
|
-
recognition: `python-${pattern.module}`,
|
|
1874
|
-
storageSystem: pattern.storageSystem,
|
|
1875
|
-
scope: pattern.module,
|
|
1876
|
-
container: access.table
|
|
1877
|
-
}),
|
|
1878
|
-
callee: field(call, "function")?.text ?? "",
|
|
1879
|
-
interaction: {
|
|
1880
|
-
class: "storage-access",
|
|
1881
|
-
kind: access.kind,
|
|
1882
|
-
fields: access.fields,
|
|
1883
|
-
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
1884
|
-
operation: field(call, "function")?.text ?? ""
|
|
1885
|
-
}
|
|
1886
|
-
});
|
|
1931
|
+
if (peeled.type === "subscript") {
|
|
1932
|
+
const object = field(peeled, "value");
|
|
1933
|
+
const index = field(peeled, "subscript");
|
|
1934
|
+
if (object === null || index === null || index.type === "slice") {
|
|
1935
|
+
return { kind: "opaque" };
|
|
1887
1936
|
}
|
|
1937
|
+
return { kind: "element", object, index };
|
|
1888
1938
|
}
|
|
1889
|
-
|
|
1890
|
-
}
|
|
1891
|
-
function patternFor(call, options) {
|
|
1892
|
-
const callee = field(call, "function");
|
|
1893
|
-
if (callee === null || callee.type !== "identifier") {
|
|
1894
|
-
return void 0;
|
|
1939
|
+
if (peeled.type === "list" || peeled.type === "tuple" || peeled.type === "expression_list") {
|
|
1940
|
+
return { kind: "array", items: children(peeled).map(elementOf) };
|
|
1895
1941
|
}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
return void 0;
|
|
1942
|
+
if (peeled.type === "dictionary") {
|
|
1943
|
+
return { kind: "record", fields: children(peeled).flatMap(fieldOf) };
|
|
1899
1944
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
(pattern) => pattern.module === module && pattern.functions.includes(callee.text)
|
|
1903
|
-
);
|
|
1904
|
-
}
|
|
1905
|
-
function statementIn(call) {
|
|
1906
|
-
const args = field(call, "arguments");
|
|
1907
|
-
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
1908
|
-
if (first === null || first.type !== "string") {
|
|
1909
|
-
return null;
|
|
1945
|
+
if (peeled.type === "call") {
|
|
1946
|
+
return callExpression(peeled, originOf2);
|
|
1910
1947
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
const statement = sqlFromParts(parts);
|
|
1925
|
-
return statement.trim() === "" ? null : statement;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
// src/storage.ts
|
|
1929
|
-
import { storageBinding as storageBinding2 } from "@suss/ir-core";
|
|
1930
|
-
function receiverCall(call) {
|
|
1931
|
-
const callee = field(call, "function");
|
|
1932
|
-
if (callee === null || callee.type !== "attribute") {
|
|
1933
|
-
return null;
|
|
1948
|
+
if (peeled.type === "binary_operator" || peeled.type === "boolean_operator") {
|
|
1949
|
+
return fromFields(
|
|
1950
|
+
peeled,
|
|
1951
|
+
["left", "operator", "right"],
|
|
1952
|
+
OPAQUE,
|
|
1953
|
+
(left, operator, right) => ({
|
|
1954
|
+
kind: "operator",
|
|
1955
|
+
operator: operator.text,
|
|
1956
|
+
operands: [left, right]
|
|
1957
|
+
})
|
|
1958
|
+
);
|
|
1934
1959
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
function methodName(call) {
|
|
1939
|
-
const callee = field(call, "function");
|
|
1940
|
-
if (callee === null || callee.type !== "attribute") {
|
|
1941
|
-
return callee?.text ?? "";
|
|
1960
|
+
if (peeled.type === "comparison_operator") {
|
|
1961
|
+
return comparisonExpression(peeled);
|
|
1942
1962
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1963
|
+
if (peeled.type === "not_operator") {
|
|
1964
|
+
return fromFields(
|
|
1965
|
+
peeled,
|
|
1966
|
+
["argument"],
|
|
1967
|
+
OPAQUE,
|
|
1968
|
+
(argument) => ({
|
|
1969
|
+
kind: "operator",
|
|
1970
|
+
operator: "not",
|
|
1971
|
+
operands: [argument]
|
|
1972
|
+
})
|
|
1973
|
+
);
|
|
1949
1974
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
return receiver === null ? call : rootOf(receiver);
|
|
1955
|
-
}
|
|
1956
|
-
function chainsIn(calls) {
|
|
1957
|
-
const endsWith = /* @__PURE__ */ new Map();
|
|
1958
|
-
for (const call of calls) {
|
|
1959
|
-
const receiver = receiverCall(call);
|
|
1960
|
-
if (receiver === null) {
|
|
1961
|
-
continue;
|
|
1962
|
-
}
|
|
1963
|
-
const start = rootOf(receiver);
|
|
1964
|
-
const kept = endsWith.get(start.id);
|
|
1965
|
-
if (kept === void 0 || call.endIndex > kept.endIndex) {
|
|
1966
|
-
endsWith.set(start.id, call);
|
|
1975
|
+
if (peeled.type === "conditional_expression") {
|
|
1976
|
+
const [whenTrue, condition, whenFalse] = children(peeled);
|
|
1977
|
+
if (whenTrue === void 0 || condition === void 0 || whenFalse === void 0) {
|
|
1978
|
+
return { kind: "opaque" };
|
|
1967
1979
|
}
|
|
1980
|
+
return { kind: "conditional", condition, whenTrue, whenFalse };
|
|
1968
1981
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
if (receiverCall(call) !== null) {
|
|
1972
|
-
continue;
|
|
1973
|
-
}
|
|
1974
|
-
const last = endsWith.get(call.id) ?? call;
|
|
1975
|
-
chains.push({
|
|
1976
|
-
root: call,
|
|
1977
|
-
last,
|
|
1978
|
-
subject: subjectOf(call),
|
|
1979
|
-
operation: methodName(last),
|
|
1980
|
-
between: calls.filter(
|
|
1981
|
-
(other) => other !== call && other !== last && other.startIndex >= call.startIndex && other.endIndex <= last.endIndex
|
|
1982
|
-
)
|
|
1983
|
-
});
|
|
1982
|
+
if (peeled.type === "lambda") {
|
|
1983
|
+
return { kind: "function", node: peeled };
|
|
1984
1984
|
}
|
|
1985
|
-
return
|
|
1985
|
+
return { kind: "opaque" };
|
|
1986
1986
|
}
|
|
1987
|
-
function
|
|
1988
|
-
const
|
|
1989
|
-
if (
|
|
1990
|
-
return
|
|
1991
|
-
}
|
|
1992
|
-
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
1993
|
-
if (from === void 0) {
|
|
1994
|
-
return void 0;
|
|
1987
|
+
function stringExpression(node) {
|
|
1988
|
+
const plain = stringLiteralValue(node);
|
|
1989
|
+
if (plain !== null) {
|
|
1990
|
+
return { kind: "literal", value: plain };
|
|
1995
1991
|
}
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
}
|
|
2000
|
-
function fieldsOf(chain) {
|
|
2001
|
-
const args = field(chain.root, "arguments");
|
|
2002
|
-
const named = [];
|
|
2003
|
-
for (const argument of args === null ? [] : children(args)) {
|
|
2004
|
-
if (argument.type === "keyword_argument") {
|
|
2005
|
-
const name = field(argument, "name");
|
|
2006
|
-
if (name !== null) {
|
|
2007
|
-
named.push(name.text);
|
|
2008
|
-
}
|
|
1992
|
+
const parts = [];
|
|
1993
|
+
for (const child of children(node)) {
|
|
1994
|
+
if (child.type === "string_content") {
|
|
1995
|
+
parts.push({ text: stringContentValue(child) });
|
|
2009
1996
|
continue;
|
|
2010
1997
|
}
|
|
2011
|
-
if (
|
|
2012
|
-
const
|
|
2013
|
-
if (
|
|
2014
|
-
|
|
1998
|
+
if (child.type === "interpolation") {
|
|
1999
|
+
const expression = field(child, "expression") ?? children(child)[0];
|
|
2000
|
+
if (expression !== void 0) {
|
|
2001
|
+
parts.push({ expression });
|
|
2015
2002
|
}
|
|
2016
2003
|
}
|
|
2017
2004
|
}
|
|
2018
|
-
return
|
|
2005
|
+
return { kind: "template", parts };
|
|
2019
2006
|
}
|
|
2020
|
-
function
|
|
2021
|
-
const
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2007
|
+
function comparisonExpression(node) {
|
|
2008
|
+
const operands = children(node);
|
|
2009
|
+
const operators = node.children.filter(
|
|
2010
|
+
(child) => child !== null && !child.isNamed
|
|
2011
|
+
);
|
|
2012
|
+
const operator = operators[0];
|
|
2013
|
+
if (operands.length !== 2 || operators.length !== 1 || operator === void 0) {
|
|
2014
|
+
return { kind: "opaque" };
|
|
2030
2015
|
}
|
|
2031
|
-
return picked;
|
|
2032
|
-
}
|
|
2033
|
-
function effectFor(pattern, chain, options) {
|
|
2034
|
-
const picked = [
|
|
2035
|
-
...new Set([chain.last, ...chain.between].flatMap(selectorOf))
|
|
2036
|
-
];
|
|
2037
2016
|
return {
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
storageSystem: pattern.storageSystem,
|
|
2042
|
-
scope: pattern.module,
|
|
2043
|
-
container: chain.subject
|
|
2044
|
-
}),
|
|
2045
|
-
callee: chain.last.text,
|
|
2046
|
-
interaction: {
|
|
2047
|
-
class: "storage-access",
|
|
2048
|
-
kind: pattern.writes.includes(chain.operation) ? "write" : "read",
|
|
2049
|
-
fields: fieldsOf(chain),
|
|
2050
|
-
operation: chain.operation,
|
|
2051
|
-
...picked.length > 0 ? { selector: picked } : {}
|
|
2052
|
-
}
|
|
2017
|
+
kind: "operator",
|
|
2018
|
+
operator: operator.text,
|
|
2019
|
+
operands: [operands[0], operands[1]]
|
|
2053
2020
|
};
|
|
2054
2021
|
}
|
|
2055
|
-
function
|
|
2056
|
-
|
|
2057
|
-
|
|
2022
|
+
function callExpression(node, originOf2) {
|
|
2023
|
+
return fromFields(
|
|
2024
|
+
node,
|
|
2025
|
+
["function", "arguments"],
|
|
2026
|
+
OPAQUE,
|
|
2027
|
+
(callee, argumentList) => {
|
|
2028
|
+
const peeledCallee = peelValue(callee);
|
|
2029
|
+
const args = argumentList.type === "argument_list" ? argumentsOf(argumentList) : [];
|
|
2030
|
+
const isMethod2 = peeledCallee.type === "attribute";
|
|
2031
|
+
return {
|
|
2032
|
+
kind: "call",
|
|
2033
|
+
callee: {
|
|
2034
|
+
receiver: isMethod2 ? field(peeledCallee, "object") : null,
|
|
2035
|
+
name: calleeName(peeledCallee),
|
|
2036
|
+
origin: () => originOf2(peeledCallee)
|
|
2037
|
+
},
|
|
2038
|
+
args,
|
|
2039
|
+
constructs: false
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
);
|
|
2058
2043
|
}
|
|
2059
|
-
function
|
|
2060
|
-
if (
|
|
2061
|
-
return
|
|
2044
|
+
function calleeName(callee) {
|
|
2045
|
+
if (callee.type === "identifier") {
|
|
2046
|
+
return callee.text;
|
|
2062
2047
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
return null;
|
|
2048
|
+
if (callee.type === "attribute") {
|
|
2049
|
+
return field(callee, "attribute")?.text ?? null;
|
|
2066
2050
|
}
|
|
2067
|
-
return
|
|
2051
|
+
return null;
|
|
2068
2052
|
}
|
|
2069
|
-
function
|
|
2070
|
-
return
|
|
2053
|
+
function argumentsOf(argumentList) {
|
|
2054
|
+
return children(argumentList).flatMap((argument) => {
|
|
2055
|
+
if (argument.type === "dictionary_splat") {
|
|
2056
|
+
return [];
|
|
2057
|
+
}
|
|
2058
|
+
if (argument.type === "keyword_argument") {
|
|
2059
|
+
const name = field(argument, "name");
|
|
2060
|
+
const value = field(argument, "value");
|
|
2061
|
+
return name === null || value === null ? [] : [{ kind: "named", name: name.text, node: value }];
|
|
2062
|
+
}
|
|
2063
|
+
return [elementOf(argument)];
|
|
2064
|
+
});
|
|
2071
2065
|
}
|
|
2072
|
-
function
|
|
2073
|
-
|
|
2074
|
-
|
|
2066
|
+
function hasDictionarySplat(call) {
|
|
2067
|
+
const argumentList = field(call, "arguments");
|
|
2068
|
+
if (argumentList === null) {
|
|
2069
|
+
return false;
|
|
2075
2070
|
}
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
);
|
|
2079
|
-
const chains = chainsIn(calls).filter(
|
|
2080
|
-
(chain) => options.couldMatch.has(methodName(chain.root)) || named.has(methodName(chain.root))
|
|
2071
|
+
return children(argumentList).some(
|
|
2072
|
+
(argument) => argument.type === "dictionary_splat"
|
|
2081
2073
|
);
|
|
2082
|
-
|
|
2074
|
+
}
|
|
2075
|
+
function elementOf(node) {
|
|
2076
|
+
if (node.type === "list_splat") {
|
|
2077
|
+
const inner = children(node)[0];
|
|
2078
|
+
return inner === void 0 ? { kind: "value", node } : { kind: "spread", node: inner };
|
|
2079
|
+
}
|
|
2080
|
+
return { kind: "value", node };
|
|
2081
|
+
}
|
|
2082
|
+
function fieldOf(node) {
|
|
2083
|
+
if (node.type === "dictionary_splat") {
|
|
2084
|
+
const inner = children(node)[0];
|
|
2085
|
+
return inner === void 0 ? [] : [{ kind: "spread", node: inner }];
|
|
2086
|
+
}
|
|
2087
|
+
if (node.type !== "pair") {
|
|
2083
2088
|
return [];
|
|
2084
2089
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2090
|
+
return fromFields(node, ["key", "value"], [], (key, value) => {
|
|
2091
|
+
const name = stringLiteralValue(key);
|
|
2092
|
+
return name === null ? [{ kind: "computed", name: key, value }] : [{ kind: "field", name, value }];
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
var STATEMENT_TYPES = {
|
|
2096
|
+
expression_statement: expressionStatement,
|
|
2097
|
+
if_statement: ifStatement,
|
|
2098
|
+
elif_clause: ifStatement,
|
|
2099
|
+
for_statement: loopStatement,
|
|
2100
|
+
while_statement: loopStatement,
|
|
2101
|
+
return_statement: (node) => ({
|
|
2102
|
+
kind: "return",
|
|
2103
|
+
value: children(node)[0] ?? null
|
|
2104
|
+
}),
|
|
2105
|
+
try_statement: (node) => ({
|
|
2106
|
+
kind: "block",
|
|
2107
|
+
body: [
|
|
2108
|
+
...blockStatements2(field(node, "body")),
|
|
2109
|
+
...children(node).flatMap(
|
|
2110
|
+
(clause) => clause.type === "finally_clause" ? blockStatements2(children(clause)[0] ?? null) : []
|
|
2111
|
+
)
|
|
2112
|
+
]
|
|
2113
|
+
}),
|
|
2114
|
+
with_statement: (node) => ({
|
|
2115
|
+
kind: "block",
|
|
2116
|
+
body: blockStatements2(field(node, "body"))
|
|
2117
|
+
})
|
|
2118
|
+
};
|
|
2119
|
+
function statementOf(node) {
|
|
2120
|
+
const lower = STATEMENT_TYPES[node.type];
|
|
2121
|
+
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2122
|
+
}
|
|
2123
|
+
function expressionStatement(node) {
|
|
2124
|
+
const inner = children(node)[0];
|
|
2125
|
+
if (inner === void 0) {
|
|
2126
|
+
return { kind: "opaque" };
|
|
2127
|
+
}
|
|
2128
|
+
if (inner.type === "assignment") {
|
|
2129
|
+
return assignment(inner, null);
|
|
2130
|
+
}
|
|
2131
|
+
if (inner.type === "augmented_assignment") {
|
|
2132
|
+
const operator = field(inner, "operator");
|
|
2133
|
+
return assignment(
|
|
2134
|
+
inner,
|
|
2135
|
+
operator === null ? null : operator.text.slice(0, -1)
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
return { kind: "expression", value: inner };
|
|
2139
|
+
}
|
|
2140
|
+
function assignment(node, operator) {
|
|
2141
|
+
return fromFields(
|
|
2142
|
+
node,
|
|
2143
|
+
["left", "right"],
|
|
2144
|
+
OPAQUE,
|
|
2145
|
+
(target, value) => {
|
|
2146
|
+
if (target.type === "pattern_list" || target.type === "tuple_pattern") {
|
|
2147
|
+
return {
|
|
2148
|
+
kind: "declare",
|
|
2149
|
+
bindings: patternNames(target).map((name) => ({ name, value: null }))
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
return { kind: "assign", target, operator, value };
|
|
2099
2153
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
function patternNames(pattern) {
|
|
2157
|
+
return children(pattern).flatMap(
|
|
2158
|
+
(child) => child.type === "identifier" ? [child.text] : patternNames(child)
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
function ifStatement(node) {
|
|
2162
|
+
return fromFields(
|
|
2163
|
+
node,
|
|
2164
|
+
["condition", "consequence"],
|
|
2165
|
+
OPAQUE,
|
|
2166
|
+
(condition, consequence) => ({
|
|
2167
|
+
kind: "branch",
|
|
2168
|
+
condition,
|
|
2169
|
+
arms: [blockStatements2(consequence), otherArm(node)]
|
|
2170
|
+
})
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
function otherArm(clause) {
|
|
2174
|
+
const statement = clause.type === "if_statement" ? clause : clause.parent;
|
|
2175
|
+
const clauses = statement === null ? [] : children(statement);
|
|
2176
|
+
const next = clauses.slice(clauses.findIndex((child) => child.id === clause.id) + 1).find(
|
|
2177
|
+
(child) => child.type === "elif_clause" || child.type === "else_clause"
|
|
2178
|
+
);
|
|
2179
|
+
if (next === void 0) {
|
|
2180
|
+
return [];
|
|
2181
|
+
}
|
|
2182
|
+
return next.type === "elif_clause" ? [next] : blockStatements2(field(next, "body"));
|
|
2183
|
+
}
|
|
2184
|
+
function loopStatement(node) {
|
|
2185
|
+
return { kind: "loop", body: blockStatements2(field(node, "body")) };
|
|
2186
|
+
}
|
|
2187
|
+
function blockStatements2(block) {
|
|
2188
|
+
return block === null ? [] : children(block);
|
|
2189
|
+
}
|
|
2190
|
+
function isRoot(node) {
|
|
2191
|
+
return ROOT_TYPES.has(node.type);
|
|
2192
|
+
}
|
|
2193
|
+
function isPathStatement(node, parent) {
|
|
2194
|
+
return parent.type === "module" || parent.type === "block" || node.type === "elif_clause";
|
|
2195
|
+
}
|
|
2196
|
+
function siteOf(node) {
|
|
2197
|
+
const path6 = [];
|
|
2198
|
+
let current = node;
|
|
2199
|
+
for (; ; ) {
|
|
2200
|
+
const parent = current.parent;
|
|
2201
|
+
if (parent === null) {
|
|
2202
|
+
return null;
|
|
2104
2203
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
if (settled === void 0) {
|
|
2108
|
-
continue;
|
|
2204
|
+
if (isPathStatement(current, parent)) {
|
|
2205
|
+
path6.unshift(current);
|
|
2109
2206
|
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
continue;
|
|
2207
|
+
if (isRoot(parent)) {
|
|
2208
|
+
return { root: parent, path: path6 };
|
|
2113
2209
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2210
|
+
current = parent;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
function functionOf(node) {
|
|
2214
|
+
if (node.type === "module") {
|
|
2215
|
+
return { parameters: [], body: children(node) };
|
|
2216
|
+
}
|
|
2217
|
+
if (node.type === "lambda") {
|
|
2218
|
+
const body = field(node, "body");
|
|
2219
|
+
if (body === null) {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
return { parameters: parameterShapes(node), body: { expression: body } };
|
|
2223
|
+
}
|
|
2224
|
+
if (node.type !== "function_definition") {
|
|
2225
|
+
return null;
|
|
2226
|
+
}
|
|
2227
|
+
const parameters = parameterShapes(node);
|
|
2228
|
+
return {
|
|
2229
|
+
parameters: isMethod(node) ? parameters.slice(1) : parameters,
|
|
2230
|
+
body: blockStatements2(field(node, "body"))
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
function isMethod(fn) {
|
|
2234
|
+
const owner = fn.parent?.type === "decorated_definition" ? fn.parent.parent : fn.parent;
|
|
2235
|
+
return owner?.type === "block" && owner.parent?.type === "class_definition";
|
|
2236
|
+
}
|
|
2237
|
+
function namesMutatedInNestedFunctions(root, readingMethods) {
|
|
2238
|
+
const mutated = /* @__PURE__ */ new Set();
|
|
2239
|
+
const visit = (node, nested) => {
|
|
2240
|
+
const name = writtenNameOf(node, readingMethods);
|
|
2241
|
+
if (nested && name !== null) {
|
|
2242
|
+
mutated.add(name);
|
|
2243
|
+
}
|
|
2244
|
+
for (const child of children(node)) {
|
|
2245
|
+
visit(child, nested || isRoot(child));
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
for (const child of children(root)) {
|
|
2249
|
+
visit(child, isRoot(child));
|
|
2250
|
+
}
|
|
2251
|
+
return mutated;
|
|
2252
|
+
}
|
|
2253
|
+
function writtenNameOf(node, readingMethods) {
|
|
2254
|
+
if (node.type === "assignment" || node.type === "augmented_assignment") {
|
|
2255
|
+
const target = field(node, "left");
|
|
2256
|
+
return target?.type === "identifier" ? target.text : null;
|
|
2257
|
+
}
|
|
2258
|
+
if (node.type === "call") {
|
|
2259
|
+
const callee = field(node, "function");
|
|
2260
|
+
if (callee?.type !== "attribute") {
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2263
|
+
const receiver = field(callee, "object");
|
|
2264
|
+
const method = field(callee, "attribute");
|
|
2265
|
+
if (receiver?.type === "identifier" && method !== null && !readingMethods.has(method.text)) {
|
|
2266
|
+
return receiver.text;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return null;
|
|
2270
|
+
}
|
|
2271
|
+
var DECLARING_PARENTS = /* @__PURE__ */ new Set([
|
|
2272
|
+
"parameters",
|
|
2273
|
+
"default_parameter",
|
|
2274
|
+
"typed_parameter",
|
|
2275
|
+
"typed_default_parameter",
|
|
2276
|
+
"lambda_parameters",
|
|
2277
|
+
"pattern_list",
|
|
2278
|
+
"tuple_pattern",
|
|
2279
|
+
"for_statement"
|
|
2280
|
+
]);
|
|
2281
|
+
function freeNamesOf(fn) {
|
|
2282
|
+
const declared = /* @__PURE__ */ new Set();
|
|
2283
|
+
const read = /* @__PURE__ */ new Set();
|
|
2284
|
+
const visit = (node) => {
|
|
2285
|
+
for (const child of children(node)) {
|
|
2286
|
+
if (child.type !== "identifier") {
|
|
2287
|
+
visit(child);
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
if (node.type === "attribute" && field(node, "attribute")?.id === child.id) {
|
|
2291
|
+
continue;
|
|
2292
|
+
}
|
|
2293
|
+
if (node.type === "keyword_argument" && field(node, "name")?.id === child.id) {
|
|
2294
|
+
continue;
|
|
2295
|
+
}
|
|
2296
|
+
if (DECLARING_PARENTS.has(node.type) || node.type === "assignment" && field(node, "left")?.id === child.id) {
|
|
2297
|
+
declared.add(child.text);
|
|
2298
|
+
continue;
|
|
2299
|
+
}
|
|
2300
|
+
read.add(child.text);
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
visit(fn);
|
|
2304
|
+
return [...read].filter((name) => !declared.has(name));
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
// src/values/origin.ts
|
|
2308
|
+
function originOf(callee, module) {
|
|
2309
|
+
const chain = memberChainOf(callee);
|
|
2310
|
+
if (chain === null) {
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
const [rootName, ...rest] = chain;
|
|
2314
|
+
if (rootName === void 0) {
|
|
2315
|
+
return null;
|
|
2316
|
+
}
|
|
2317
|
+
const binding = resolveName(scopeAt(callee, module), rootName);
|
|
2318
|
+
if (binding === null) {
|
|
2319
|
+
return rest.length === 0 ? { module: "builtins", name: rootName } : null;
|
|
2320
|
+
}
|
|
2321
|
+
const path6 = importedPathOf(binding, rootName);
|
|
2322
|
+
if (path6 === null) {
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
const dotted = [...path6, ...rest];
|
|
2326
|
+
const name = dotted.pop();
|
|
2327
|
+
if (name === void 0 || dotted.length === 0) {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
return { module: dotted.join("."), name };
|
|
2331
|
+
}
|
|
2332
|
+
function importedPathOf(binding, localName) {
|
|
2333
|
+
if (binding.kind === "importFrom") {
|
|
2334
|
+
if (binding.relativeLevel > 0) {
|
|
2335
|
+
return null;
|
|
2336
|
+
}
|
|
2337
|
+
return [...binding.module.split("."), binding.importedName];
|
|
2338
|
+
}
|
|
2339
|
+
if (binding.kind !== "import") {
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
const unaliasedDotted = binding.module !== localName && binding.module.startsWith(`${localName}.`);
|
|
2343
|
+
return unaliasedDotted ? [localName] : binding.module.split(".");
|
|
2344
|
+
}
|
|
2345
|
+
function memberChainOf(node) {
|
|
2346
|
+
const names = [];
|
|
2347
|
+
let current = peelValue(node);
|
|
2348
|
+
while (current.type === "attribute") {
|
|
2349
|
+
const object = field(current, "object");
|
|
2350
|
+
const attribute = field(current, "attribute");
|
|
2351
|
+
if (object === null || attribute === null) {
|
|
2352
|
+
return null;
|
|
2353
|
+
}
|
|
2354
|
+
names.unshift(attribute.text);
|
|
2355
|
+
current = peelValue(object);
|
|
2356
|
+
}
|
|
2357
|
+
if (current.type !== "identifier") {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
return [current.text, ...names];
|
|
2361
|
+
}
|
|
2362
|
+
function scopeAt(node, module) {
|
|
2363
|
+
let current = node;
|
|
2364
|
+
while (current !== null) {
|
|
2365
|
+
const scope = module.scopeFor.get(current.id);
|
|
2366
|
+
if (scope !== void 0) {
|
|
2367
|
+
return scope;
|
|
2368
|
+
}
|
|
2369
|
+
current = current.parent;
|
|
2370
|
+
}
|
|
2371
|
+
return module.moduleScope;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// src/values/rows.ts
|
|
2375
|
+
import {
|
|
2376
|
+
appended,
|
|
2377
|
+
concat,
|
|
2378
|
+
constant,
|
|
2379
|
+
environmentRead,
|
|
2380
|
+
equals,
|
|
2381
|
+
extended,
|
|
2382
|
+
fallback,
|
|
2383
|
+
formatArguments,
|
|
2384
|
+
hole,
|
|
2385
|
+
joined,
|
|
2386
|
+
joinedPath,
|
|
2387
|
+
literalOf,
|
|
2388
|
+
negated,
|
|
2389
|
+
operand,
|
|
2390
|
+
percentFormatted,
|
|
2391
|
+
plus,
|
|
2392
|
+
readableFallback,
|
|
2393
|
+
stripped,
|
|
2394
|
+
text,
|
|
2395
|
+
truthOf
|
|
2396
|
+
} from "@suss/values";
|
|
2397
|
+
var operatorRows = [
|
|
2398
|
+
{
|
|
2399
|
+
kind: "operator",
|
|
2400
|
+
operator: "+",
|
|
2401
|
+
arity: 2,
|
|
2402
|
+
apply: ([a, b]) => plus(operand(a), operand(b))
|
|
2403
|
+
},
|
|
2404
|
+
{
|
|
2405
|
+
kind: "operator",
|
|
2406
|
+
operator: "%",
|
|
2407
|
+
arity: 2,
|
|
2408
|
+
apply: ([a, b], contentOf) => percentFormatted(operand(a), formatArguments(contentOf(operand(b))))
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
kind: "operator",
|
|
2412
|
+
operator: "==",
|
|
2413
|
+
arity: 2,
|
|
2414
|
+
apply: ([a, b]) => equals(operand(a), operand(b))
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
kind: "operator",
|
|
2418
|
+
operator: "!=",
|
|
2419
|
+
arity: 2,
|
|
2420
|
+
apply: ([a, b]) => negated(equals(operand(a), operand(b)))
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
kind: "operator",
|
|
2424
|
+
operator: "is",
|
|
2425
|
+
arity: 2,
|
|
2426
|
+
apply: ([a, b]) => equals(operand(a), operand(b))
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
kind: "operator",
|
|
2430
|
+
operator: "is not",
|
|
2431
|
+
arity: 2,
|
|
2432
|
+
apply: ([a, b]) => negated(equals(operand(a), operand(b)))
|
|
2433
|
+
},
|
|
2434
|
+
{
|
|
2435
|
+
kind: "operator",
|
|
2436
|
+
operator: "not",
|
|
2437
|
+
arity: 1,
|
|
2438
|
+
apply: ([a]) => negated(operand(a))
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
kind: "operator",
|
|
2442
|
+
operator: "or",
|
|
2443
|
+
arity: 2,
|
|
2444
|
+
apply: ([a, b]) => readableFallback(operand(a), operand(b), truthOf)
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
kind: "operator",
|
|
2448
|
+
operator: "and",
|
|
2449
|
+
arity: 2,
|
|
2450
|
+
apply: ([a, b]) => fallback(operand(a), operand(b), (left) => {
|
|
2451
|
+
const truth = truthOf(left);
|
|
2452
|
+
return truth === null ? null : !truth;
|
|
2453
|
+
})
|
|
2454
|
+
}
|
|
2455
|
+
];
|
|
2456
|
+
var stringRows = [
|
|
2457
|
+
{
|
|
2458
|
+
kind: "method",
|
|
2459
|
+
method: "join",
|
|
2460
|
+
on: "string",
|
|
2461
|
+
apply: ({ receiver, args, contentOf }) => ({
|
|
2462
|
+
result: joined(contentOf(operand(args[0])), operand(receiver))
|
|
2463
|
+
})
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
kind: "method",
|
|
2467
|
+
method: "format",
|
|
2468
|
+
on: "string",
|
|
2469
|
+
apply: ({ receiver, args }) => ({
|
|
2470
|
+
result: braceFormatted(operand(receiver), args)
|
|
2471
|
+
})
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
kind: "method",
|
|
2475
|
+
method: "strip",
|
|
2476
|
+
on: "string",
|
|
2477
|
+
apply: ({ receiver, args }) => ({
|
|
2478
|
+
result: args.length === 0 ? stripped(operand(receiver)) : hole("value")
|
|
2479
|
+
})
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
kind: "method",
|
|
2483
|
+
method: "rstrip",
|
|
2484
|
+
on: "string",
|
|
2485
|
+
apply: ({ receiver, args }) => ({
|
|
2486
|
+
result: args.length === 0 ? stripped(operand(receiver), "end") : hole("value")
|
|
2487
|
+
})
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
kind: "method",
|
|
2491
|
+
method: "lstrip",
|
|
2492
|
+
on: "string",
|
|
2493
|
+
apply: ({ receiver, args }) => ({
|
|
2494
|
+
result: args.length === 0 ? stripped(operand(receiver), "start") : hole("value")
|
|
2495
|
+
})
|
|
2496
|
+
}
|
|
2497
|
+
];
|
|
2498
|
+
var sequenceRows = ["sequence", "unbounded"].flatMap((on) => [
|
|
2499
|
+
{
|
|
2500
|
+
kind: "method",
|
|
2501
|
+
method: "append",
|
|
2502
|
+
on,
|
|
2503
|
+
apply: ({ receiver, args }) => ({
|
|
2504
|
+
result: constant(null),
|
|
2505
|
+
receiver: appended(operand(receiver), args)
|
|
2506
|
+
})
|
|
2507
|
+
},
|
|
2508
|
+
{
|
|
2509
|
+
kind: "method",
|
|
2510
|
+
method: "extend",
|
|
2511
|
+
on,
|
|
2512
|
+
apply: ({ receiver, args, contentOf }) => ({
|
|
2513
|
+
result: constant(null),
|
|
2514
|
+
receiver: extended(operand(receiver), contentOf(operand(args[0])))
|
|
2515
|
+
})
|
|
2516
|
+
}
|
|
2517
|
+
]);
|
|
2518
|
+
var calleeRows = [
|
|
2519
|
+
{
|
|
2520
|
+
kind: "callee",
|
|
2521
|
+
origin: { module: "builtins", name: "str" },
|
|
2522
|
+
apply: ({ args }) => ({ result: concat([operand(args[0])]) })
|
|
2523
|
+
},
|
|
2524
|
+
{
|
|
2525
|
+
kind: "callee",
|
|
2526
|
+
origin: { module: "os.path", name: "join" },
|
|
2527
|
+
apply: ({ args }) => ({ result: joinedPath(args) })
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
kind: "callee",
|
|
2531
|
+
origin: { module: "os", name: "getenv" },
|
|
2532
|
+
apply: ({ args }) => ({ result: environmentRead(args) })
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
kind: "callee",
|
|
2536
|
+
origin: { module: "os.environ", name: "get" },
|
|
2537
|
+
apply: ({ args }) => ({ result: environmentRead(args) })
|
|
2538
|
+
}
|
|
2539
|
+
];
|
|
2540
|
+
var BRACE_PLACEHOLDER = /\{([^{}]*)\}/g;
|
|
2541
|
+
function braceFormatted(receiver, args) {
|
|
2542
|
+
const template = literalOf(receiver);
|
|
2543
|
+
if (template === null) {
|
|
2544
|
+
return hole("value");
|
|
2545
|
+
}
|
|
2546
|
+
const parts = [];
|
|
2547
|
+
let last = 0;
|
|
2548
|
+
let position = 0;
|
|
2549
|
+
for (const match of template.matchAll(BRACE_PLACEHOLDER)) {
|
|
2550
|
+
parts.push(text(template.slice(last, match.index)));
|
|
2551
|
+
const argument = args[position];
|
|
2552
|
+
const placeholder = match[1] ?? "";
|
|
2553
|
+
parts.push(
|
|
2554
|
+
argument === void 0 ? hole(
|
|
2555
|
+
placeholder === "" ? "value" : placeholder.split(/[!:]/)[0] ?? "value"
|
|
2556
|
+
) : concat([argument])
|
|
2118
2557
|
);
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
}
|
|
2122
|
-
effects.push(effectFor(pattern, chain, options));
|
|
2558
|
+
position += 1;
|
|
2559
|
+
last = match.index + match[0].length;
|
|
2123
2560
|
}
|
|
2124
|
-
|
|
2561
|
+
parts.push(text(template.slice(last)));
|
|
2562
|
+
return concat(parts);
|
|
2125
2563
|
}
|
|
2564
|
+
var pythonRows = [
|
|
2565
|
+
...operatorRows,
|
|
2566
|
+
...stringRows,
|
|
2567
|
+
...sequenceRows,
|
|
2568
|
+
...calleeRows
|
|
2569
|
+
];
|
|
2126
2570
|
|
|
2127
2571
|
// src/values/evaluator.ts
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
rows.flatMap(
|
|
2144
|
-
(row) => row.kind === "method" && !WRITING_ROWS.has(row.method) ? [row.method] : []
|
|
2572
|
+
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2573
|
+
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2574
|
+
var modulesByTree = /* @__PURE__ */ new WeakMap();
|
|
2575
|
+
function bindEvaluator(db, nodes) {
|
|
2576
|
+
for (const entry of nodes.files) {
|
|
2577
|
+
modulesByTree.set(entry.root.tree, entry.module);
|
|
2578
|
+
}
|
|
2579
|
+
evaluators.set(
|
|
2580
|
+
db,
|
|
2581
|
+
new Evaluator(
|
|
2582
|
+
pythonLowering({
|
|
2583
|
+
context: contextOver(db, nodes),
|
|
2584
|
+
originOf: calleeOrigin,
|
|
2585
|
+
rows: pythonRows
|
|
2586
|
+
})
|
|
2145
2587
|
)
|
|
2146
2588
|
);
|
|
2147
2589
|
}
|
|
2148
|
-
function
|
|
2149
|
-
|
|
2150
|
-
const readingMethods = readingMethodsOf(rows);
|
|
2151
|
-
const mutatedByRoot = /* @__PURE__ */ new Map();
|
|
2152
|
-
return {
|
|
2153
|
-
idOf: (node) => node.id,
|
|
2154
|
-
expression: (node) => expressionOf(node, originOf2),
|
|
2155
|
-
statement: statementOf,
|
|
2156
|
-
siteOf,
|
|
2157
|
-
functionOf,
|
|
2158
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
2159
|
-
callable: (node) => {
|
|
2160
|
-
if (context === null || hasDictionarySplat(node)) {
|
|
2161
|
-
return null;
|
|
2162
|
-
}
|
|
2163
|
-
return context.callable(node);
|
|
2164
|
-
},
|
|
2165
|
-
mutatedInNestedFunction: (root, name) => {
|
|
2166
|
-
let mutated = mutatedByRoot.get(root.id);
|
|
2167
|
-
if (mutated === void 0) {
|
|
2168
|
-
mutated = namesMutatedInNestedFunctions(root, readingMethods);
|
|
2169
|
-
mutatedByRoot.set(root.id, mutated);
|
|
2170
|
-
}
|
|
2171
|
-
return mutated.has(name);
|
|
2172
|
-
},
|
|
2173
|
-
freeNamesOf,
|
|
2174
|
-
holeNameOf: placeholderName,
|
|
2175
|
-
rows
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
function placeholderName(node) {
|
|
2179
|
-
const peeled = peelValue(node);
|
|
2180
|
-
if (peeled.type === "identifier") {
|
|
2181
|
-
return peeled.text;
|
|
2182
|
-
}
|
|
2183
|
-
if (peeled.type === "attribute") {
|
|
2184
|
-
return field(peeled, "attribute")?.text ?? "param";
|
|
2185
|
-
}
|
|
2186
|
-
return "param";
|
|
2187
|
-
}
|
|
2188
|
-
function peelValue(node) {
|
|
2189
|
-
const wraps = node.type === "parenthesized_expression" || node.type === "await";
|
|
2190
|
-
const inner = wraps ? children(node)[0] : void 0;
|
|
2191
|
-
return inner === void 0 ? node : peelValue(inner);
|
|
2590
|
+
function evaluatedValue(node, db) {
|
|
2591
|
+
return force(evaluatorFor(node, db).evaluate(node));
|
|
2192
2592
|
}
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
const found = names.map((name) => field(node, name));
|
|
2196
|
-
if (found.some((part) => part === null)) {
|
|
2197
|
-
return fallback2;
|
|
2198
|
-
}
|
|
2199
|
-
return build(...found);
|
|
2593
|
+
function stringValueOf(node, db) {
|
|
2594
|
+
return literalOf2(evaluatedValue(node, db));
|
|
2200
2595
|
}
|
|
2201
|
-
function
|
|
2202
|
-
const
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
return literal(peeled);
|
|
2206
|
-
}
|
|
2207
|
-
if (peeled.type === "string") {
|
|
2208
|
-
return stringExpression(peeled);
|
|
2209
|
-
}
|
|
2210
|
-
if (peeled.type === "concatenated_string") {
|
|
2211
|
-
return {
|
|
2212
|
-
kind: "template",
|
|
2213
|
-
parts: children(peeled).map((part) => ({ expression: part }))
|
|
2214
|
-
};
|
|
2215
|
-
}
|
|
2216
|
-
if (peeled.type === "identifier") {
|
|
2217
|
-
return { kind: "name", text: peeled.text };
|
|
2218
|
-
}
|
|
2219
|
-
if (peeled.type === "attribute") {
|
|
2220
|
-
return fromFields(
|
|
2221
|
-
peeled,
|
|
2222
|
-
["object", "attribute"],
|
|
2223
|
-
OPAQUE,
|
|
2224
|
-
(object, name) => ({
|
|
2225
|
-
kind: "member",
|
|
2226
|
-
object,
|
|
2227
|
-
name: name.text
|
|
2228
|
-
})
|
|
2229
|
-
);
|
|
2230
|
-
}
|
|
2231
|
-
if (peeled.type === "subscript") {
|
|
2232
|
-
const object = field(peeled, "value");
|
|
2233
|
-
const index = field(peeled, "subscript");
|
|
2234
|
-
if (object === null || index === null || index.type === "slice") {
|
|
2235
|
-
return { kind: "opaque" };
|
|
2236
|
-
}
|
|
2237
|
-
return { kind: "element", object, index };
|
|
2238
|
-
}
|
|
2239
|
-
if (peeled.type === "list" || peeled.type === "tuple" || peeled.type === "expression_list") {
|
|
2240
|
-
return { kind: "array", items: children(peeled).map(elementOf) };
|
|
2241
|
-
}
|
|
2242
|
-
if (peeled.type === "dictionary") {
|
|
2243
|
-
return { kind: "record", fields: children(peeled).flatMap(fieldOf) };
|
|
2244
|
-
}
|
|
2245
|
-
if (peeled.type === "call") {
|
|
2246
|
-
return callExpression(peeled, originOf2);
|
|
2596
|
+
function evaluatorFor(node, db) {
|
|
2597
|
+
const bound = db === void 0 ? void 0 : evaluators.get(db);
|
|
2598
|
+
if (bound !== void 0) {
|
|
2599
|
+
return bound;
|
|
2247
2600
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
operands: [left, right]
|
|
2601
|
+
const tree = node.tree;
|
|
2602
|
+
let local = withoutFacts.get(tree);
|
|
2603
|
+
if (local === void 0) {
|
|
2604
|
+
local = new Evaluator(
|
|
2605
|
+
pythonLowering({
|
|
2606
|
+
context: null,
|
|
2607
|
+
originOf: calleeOrigin,
|
|
2608
|
+
rows: pythonRows
|
|
2257
2609
|
})
|
|
2258
2610
|
);
|
|
2611
|
+
withoutFacts.set(tree, local);
|
|
2259
2612
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2613
|
+
return local;
|
|
2614
|
+
}
|
|
2615
|
+
function moduleOf(node) {
|
|
2616
|
+
const tree = node.tree;
|
|
2617
|
+
let module = modulesByTree.get(tree);
|
|
2618
|
+
if (module === void 0) {
|
|
2619
|
+
module = bindModule(tree.rootNode);
|
|
2620
|
+
modulesByTree.set(tree, module);
|
|
2262
2621
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
);
|
|
2622
|
+
return module;
|
|
2623
|
+
}
|
|
2624
|
+
function calleeOrigin(callee) {
|
|
2625
|
+
return originOf(callee, moduleOf(callee));
|
|
2626
|
+
}
|
|
2627
|
+
function contextOver(db, nodes) {
|
|
2628
|
+
const filesByRoot = /* @__PURE__ */ new Map();
|
|
2629
|
+
const rootsByFile = /* @__PURE__ */ new Map();
|
|
2630
|
+
for (const entry of nodes.files) {
|
|
2631
|
+
filesByRoot.set(entry.root.id, entry);
|
|
2632
|
+
rootsByFile.set(entry.file, entry.root);
|
|
2274
2633
|
}
|
|
2275
|
-
|
|
2276
|
-
const
|
|
2277
|
-
if (
|
|
2278
|
-
return
|
|
2634
|
+
const keyOf2 = (node) => {
|
|
2635
|
+
const entry = filesByRoot.get(node.tree.rootNode.id);
|
|
2636
|
+
if (entry === void 0) {
|
|
2637
|
+
return null;
|
|
2279
2638
|
}
|
|
2280
|
-
return
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2639
|
+
return readKey(entry.file, node, enclosingFunction(node));
|
|
2640
|
+
};
|
|
2641
|
+
return {
|
|
2642
|
+
writtenTo: (node) => {
|
|
2643
|
+
const key = keyOf2(node);
|
|
2644
|
+
if (key === null) {
|
|
2645
|
+
return null;
|
|
2646
|
+
}
|
|
2647
|
+
const answer = writtenValueOf(db, key);
|
|
2648
|
+
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2649
|
+
},
|
|
2650
|
+
callable: (call) => {
|
|
2651
|
+
const callee = field(call, "function");
|
|
2652
|
+
const key = callee === null ? null : keyOf2(callee);
|
|
2653
|
+
if (key === null) {
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
resolveCalls(db, [key]);
|
|
2657
|
+
const resolved = db.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
2658
|
+
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2659
|
+
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2286
2662
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2663
|
+
|
|
2664
|
+
// src/paths/raisedResponses.ts
|
|
2665
|
+
var NESTED_DEFINITION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
2666
|
+
function raisedResponses(body, options) {
|
|
2667
|
+
if (body === null || options.calls.length === 0) {
|
|
2668
|
+
return [];
|
|
2291
2669
|
}
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
parts.push({ expression });
|
|
2670
|
+
const found = [];
|
|
2671
|
+
const walk = (node) => {
|
|
2672
|
+
for (const child of node.namedChildren) {
|
|
2673
|
+
if (child === null || NESTED_DEFINITION_TYPES2.has(child.type)) {
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
const response = raisedResponseOf(child, options);
|
|
2677
|
+
if (response !== null) {
|
|
2678
|
+
found.push(response);
|
|
2302
2679
|
}
|
|
2680
|
+
walk(child);
|
|
2303
2681
|
}
|
|
2682
|
+
};
|
|
2683
|
+
walk(body);
|
|
2684
|
+
return found;
|
|
2685
|
+
}
|
|
2686
|
+
var ENDING_EXPRESSION = {
|
|
2687
|
+
raise_statement: (statement) => statement.namedChildren[0] ?? null,
|
|
2688
|
+
expression_statement: (statement) => {
|
|
2689
|
+
const only = statement.namedChildren[0];
|
|
2690
|
+
return only?.type === "call" ? only : null;
|
|
2304
2691
|
}
|
|
2305
|
-
|
|
2692
|
+
};
|
|
2693
|
+
function raisedResponseOf(statement, options) {
|
|
2694
|
+
const expression = ENDING_EXPRESSION[statement.type]?.(statement) ?? null;
|
|
2695
|
+
if (expression === null) {
|
|
2696
|
+
return null;
|
|
2697
|
+
}
|
|
2698
|
+
const thrownByCall = statement.type !== "raise_statement";
|
|
2699
|
+
const call = expression.type === "call" ? expression : null;
|
|
2700
|
+
const callee = calleeOf(expression, call);
|
|
2701
|
+
const declared = declaredCallFor(callee, options);
|
|
2702
|
+
if (declared === void 0 && thrownByCall) {
|
|
2703
|
+
return null;
|
|
2704
|
+
}
|
|
2705
|
+
return {
|
|
2706
|
+
statement,
|
|
2707
|
+
thrownByCall,
|
|
2708
|
+
terminal: terminalOf(statement, callee, call, declared, options.facts)
|
|
2709
|
+
};
|
|
2306
2710
|
}
|
|
2307
|
-
function
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
);
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
return { kind: "opaque" };
|
|
2711
|
+
function calleeOf(expression, call) {
|
|
2712
|
+
return call === null ? expression : field(call, "function") ?? expression;
|
|
2713
|
+
}
|
|
2714
|
+
function declaredCallFor(callee, options) {
|
|
2715
|
+
const origin = originOf(callee, options.module);
|
|
2716
|
+
if (origin === null) {
|
|
2717
|
+
return void 0;
|
|
2315
2718
|
}
|
|
2719
|
+
const qualified = `${origin.module}.${origin.name}`;
|
|
2720
|
+
return options.calls.find((call) => call.callee === qualified);
|
|
2721
|
+
}
|
|
2722
|
+
function terminalOf(statement, callee, call, declared, facts) {
|
|
2316
2723
|
return {
|
|
2317
|
-
kind: "
|
|
2318
|
-
|
|
2319
|
-
|
|
2724
|
+
kind: "throw",
|
|
2725
|
+
statusCode: declared === void 0 ? null : statusOf(declared, call, facts),
|
|
2726
|
+
body: null,
|
|
2727
|
+
exceptionType: lastSegmentOf(declared?.callee ?? callee.text),
|
|
2728
|
+
message: call === null ? null : writtenMessageOf(call),
|
|
2729
|
+
...declared === void 0 ? {} : { producesResponse: true },
|
|
2730
|
+
component: null,
|
|
2731
|
+
renderTree: null,
|
|
2732
|
+
delegateTarget: null,
|
|
2733
|
+
emitEvent: null,
|
|
2734
|
+
location: rangeOf(statement)
|
|
2320
2735
|
};
|
|
2321
2736
|
}
|
|
2322
|
-
function
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
};
|
|
2341
|
-
}
|
|
2737
|
+
function lastSegmentOf(dotted) {
|
|
2738
|
+
const segment = dotted.split(".").pop();
|
|
2739
|
+
return segment === void 0 || segment === "" ? null : segment;
|
|
2740
|
+
}
|
|
2741
|
+
function statusOf(declared, call, facts) {
|
|
2742
|
+
const written = call === null ? null : statusArgumentOf(declared, call);
|
|
2743
|
+
if (written === null) {
|
|
2744
|
+
return declared.defaultStatusCode === void 0 ? null : { type: "literal", value: declared.defaultStatusCode };
|
|
2745
|
+
}
|
|
2746
|
+
const value = constantOf(evaluatedValue(written, facts));
|
|
2747
|
+
if (typeof value === "number") {
|
|
2748
|
+
return { type: "literal", value };
|
|
2749
|
+
}
|
|
2750
|
+
return { type: "dynamic", sourceText: written.text };
|
|
2751
|
+
}
|
|
2752
|
+
function argumentsOf2(call) {
|
|
2753
|
+
return (field(call, "arguments")?.namedChildren ?? []).filter(
|
|
2754
|
+
(child) => child !== null
|
|
2342
2755
|
);
|
|
2343
2756
|
}
|
|
2344
|
-
function
|
|
2345
|
-
|
|
2346
|
-
|
|
2757
|
+
function statusArgumentOf(declared, call) {
|
|
2758
|
+
const args = argumentsOf2(call);
|
|
2759
|
+
const keyed = args.find(
|
|
2760
|
+
(arg) => arg.type === "keyword_argument" && field(arg, "name")?.text === declared.statusKeyword
|
|
2761
|
+
);
|
|
2762
|
+
if (keyed !== void 0) {
|
|
2763
|
+
return field(keyed, "value");
|
|
2347
2764
|
}
|
|
2348
|
-
if (
|
|
2349
|
-
return
|
|
2765
|
+
if (declared.statusArgument === void 0) {
|
|
2766
|
+
return null;
|
|
2767
|
+
}
|
|
2768
|
+
const positional = args.filter((arg) => arg.type !== "keyword_argument");
|
|
2769
|
+
return positional[declared.statusArgument] ?? null;
|
|
2770
|
+
}
|
|
2771
|
+
function writtenMessageOf(call) {
|
|
2772
|
+
for (const arg of argumentsOf2(call)) {
|
|
2773
|
+
const value = arg.type === "keyword_argument" ? field(arg, "value") : arg;
|
|
2774
|
+
const literal = value === null ? null : stringLiteralValue(value);
|
|
2775
|
+
if (literal !== null) {
|
|
2776
|
+
return literal;
|
|
2777
|
+
}
|
|
2350
2778
|
}
|
|
2351
2779
|
return null;
|
|
2352
2780
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2781
|
+
|
|
2782
|
+
// src/paths/returnedShape.ts
|
|
2783
|
+
var MAX_DEPTH = 6;
|
|
2784
|
+
var SCALARS = {
|
|
2785
|
+
none: { type: "null" },
|
|
2786
|
+
true: { type: "boolean" },
|
|
2787
|
+
false: { type: "boolean" }
|
|
2788
|
+
};
|
|
2789
|
+
function children2(node) {
|
|
2790
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
2791
|
+
}
|
|
2792
|
+
function recordShape(node, depth) {
|
|
2793
|
+
const properties = {};
|
|
2794
|
+
let everyKeyWritten = true;
|
|
2795
|
+
for (const pair of children2(node)) {
|
|
2796
|
+
if (pair.type !== "pair") {
|
|
2797
|
+
everyKeyWritten = false;
|
|
2798
|
+
continue;
|
|
2357
2799
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2800
|
+
const key = field(pair, "key");
|
|
2801
|
+
const value = field(pair, "value");
|
|
2802
|
+
if (key === null || value === null || key.type !== "string") {
|
|
2803
|
+
everyKeyWritten = false;
|
|
2804
|
+
continue;
|
|
2362
2805
|
}
|
|
2363
|
-
|
|
2364
|
-
});
|
|
2365
|
-
}
|
|
2366
|
-
function hasDictionarySplat(call) {
|
|
2367
|
-
const argumentList = field(call, "arguments");
|
|
2368
|
-
if (argumentList === null) {
|
|
2369
|
-
return false;
|
|
2806
|
+
properties[key.text.slice(1, -1)] = shapeOfReturned(value, depth + 1);
|
|
2370
2807
|
}
|
|
2371
|
-
return
|
|
2372
|
-
(argument) => argument.type === "dictionary_splat"
|
|
2373
|
-
);
|
|
2808
|
+
return everyKeyWritten && Object.keys(properties).length > 0 ? { type: "record", properties } : { type: "dictionary", values: { type: "unknown" } };
|
|
2374
2809
|
}
|
|
2375
|
-
function
|
|
2376
|
-
if (
|
|
2377
|
-
|
|
2378
|
-
return inner === void 0 ? { kind: "value", node } : { kind: "spread", node: inner };
|
|
2810
|
+
function shapeOfReturned(node, depth = 0) {
|
|
2811
|
+
if (depth > MAX_DEPTH) {
|
|
2812
|
+
return { type: "unknown" };
|
|
2379
2813
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
if (node.type === "dictionary_splat") {
|
|
2384
|
-
const inner = children(node)[0];
|
|
2385
|
-
return inner === void 0 ? [] : [{ kind: "spread", node: inner }];
|
|
2814
|
+
const scalar = SCALARS[node.type];
|
|
2815
|
+
if (scalar !== void 0) {
|
|
2816
|
+
return scalar;
|
|
2386
2817
|
}
|
|
2387
|
-
if (node.type
|
|
2388
|
-
return
|
|
2818
|
+
if (node.type === "string") {
|
|
2819
|
+
return { type: "literal", value: node.text.slice(1, -1) };
|
|
2389
2820
|
}
|
|
2390
|
-
|
|
2391
|
-
const
|
|
2392
|
-
return
|
|
2393
|
-
}
|
|
2821
|
+
if (node.type === "integer") {
|
|
2822
|
+
const value = Number.parseInt(node.text, 10);
|
|
2823
|
+
return Number.isNaN(value) ? { type: "integer" } : { type: "literal", value };
|
|
2824
|
+
}
|
|
2825
|
+
if (node.type === "float") {
|
|
2826
|
+
return { type: "number" };
|
|
2827
|
+
}
|
|
2828
|
+
if (node.type === "dictionary") {
|
|
2829
|
+
return recordShape(node, depth);
|
|
2830
|
+
}
|
|
2831
|
+
if (node.type === "list" || node.type === "tuple" || node.type === "set") {
|
|
2832
|
+
const first = children2(node)[0];
|
|
2833
|
+
return {
|
|
2834
|
+
type: "array",
|
|
2835
|
+
items: first === void 0 ? { type: "unknown" } : shapeOfReturned(first, depth + 1)
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
if (node.type === "identifier" || node.type === "attribute") {
|
|
2839
|
+
return { type: "ref", name: node.text };
|
|
2840
|
+
}
|
|
2841
|
+
return { type: "unknown" };
|
|
2394
2842
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
kind: "block",
|
|
2407
|
-
body: [
|
|
2408
|
-
...blockStatements2(field(node, "body")),
|
|
2409
|
-
...children(node).flatMap(
|
|
2410
|
-
(clause) => clause.type === "finally_clause" ? blockStatements2(children(clause)[0] ?? null) : []
|
|
2411
|
-
)
|
|
2412
|
-
]
|
|
2413
|
-
}),
|
|
2414
|
-
with_statement: (node) => ({
|
|
2415
|
-
kind: "block",
|
|
2416
|
-
body: blockStatements2(field(node, "body"))
|
|
2417
|
-
})
|
|
2418
|
-
};
|
|
2419
|
-
function statementOf(node) {
|
|
2420
|
-
const lower = STATEMENT_TYPES[node.type];
|
|
2421
|
-
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2843
|
+
function returnedBodyShape(statement) {
|
|
2844
|
+
const returned = statement.namedChildren[0];
|
|
2845
|
+
if (returned == null) {
|
|
2846
|
+
return null;
|
|
2847
|
+
}
|
|
2848
|
+
const value = returned.type === "expression_list" ? children2(returned)[0] : returned;
|
|
2849
|
+
if (value === void 0) {
|
|
2850
|
+
return null;
|
|
2851
|
+
}
|
|
2852
|
+
const shape = shapeOfReturned(value);
|
|
2853
|
+
return shape.type === "unknown" ? null : shape;
|
|
2422
2854
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2855
|
+
|
|
2856
|
+
// src/rawSql.ts
|
|
2857
|
+
import { storageBinding } from "@suss/ir-core";
|
|
2858
|
+
import { readSqlAccess, sqlFromParts } from "@suss/sql";
|
|
2859
|
+
function rawSqlEffects(calls, options) {
|
|
2860
|
+
if (options.patterns.length === 0) {
|
|
2861
|
+
return [];
|
|
2862
|
+
}
|
|
2863
|
+
const effects = [];
|
|
2864
|
+
for (const call of calls) {
|
|
2865
|
+
const pattern = patternFor(call, options);
|
|
2866
|
+
if (pattern === void 0) {
|
|
2867
|
+
continue;
|
|
2868
|
+
}
|
|
2869
|
+
const statement = statementIn(call);
|
|
2870
|
+
if (statement === null) {
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
for (const access of readSqlAccess(statement, {
|
|
2874
|
+
dialect: pattern.storageSystem
|
|
2875
|
+
})) {
|
|
2876
|
+
effects.push({
|
|
2877
|
+
type: "interaction",
|
|
2878
|
+
binding: storageBinding({
|
|
2879
|
+
recognition: `python-${pattern.module}`,
|
|
2880
|
+
storageSystem: pattern.storageSystem,
|
|
2881
|
+
scope: pattern.module,
|
|
2882
|
+
container: access.table
|
|
2883
|
+
}),
|
|
2884
|
+
callee: field(call, "function")?.text ?? "",
|
|
2885
|
+
interaction: {
|
|
2886
|
+
class: "storage-access",
|
|
2887
|
+
kind: access.kind,
|
|
2888
|
+
fields: access.fields,
|
|
2889
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
2890
|
+
operation: field(call, "function")?.text ?? ""
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2427
2894
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2895
|
+
return effects;
|
|
2896
|
+
}
|
|
2897
|
+
function patternFor(call, options) {
|
|
2898
|
+
const callee = field(call, "function");
|
|
2899
|
+
if (callee === null || callee.type !== "identifier") {
|
|
2900
|
+
return void 0;
|
|
2430
2901
|
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
return
|
|
2434
|
-
inner,
|
|
2435
|
-
operator === null ? null : operator.text.slice(0, -1)
|
|
2436
|
-
);
|
|
2902
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
2903
|
+
if (from === void 0) {
|
|
2904
|
+
return void 0;
|
|
2437
2905
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
return fromFields(
|
|
2442
|
-
node,
|
|
2443
|
-
["left", "right"],
|
|
2444
|
-
OPAQUE,
|
|
2445
|
-
(target, value) => {
|
|
2446
|
-
if (target.type === "pattern_list" || target.type === "tuple_pattern") {
|
|
2447
|
-
return {
|
|
2448
|
-
kind: "declare",
|
|
2449
|
-
bindings: patternNames(target).map((name) => ({ name, value: null }))
|
|
2450
|
-
};
|
|
2451
|
-
}
|
|
2452
|
-
return { kind: "assign", target, operator, value };
|
|
2453
|
-
}
|
|
2454
|
-
);
|
|
2455
|
-
}
|
|
2456
|
-
function patternNames(pattern) {
|
|
2457
|
-
return children(pattern).flatMap(
|
|
2458
|
-
(child) => child.type === "identifier" ? [child.text] : patternNames(child)
|
|
2459
|
-
);
|
|
2460
|
-
}
|
|
2461
|
-
function ifStatement(node) {
|
|
2462
|
-
return fromFields(
|
|
2463
|
-
node,
|
|
2464
|
-
["condition", "consequence"],
|
|
2465
|
-
OPAQUE,
|
|
2466
|
-
(condition, consequence) => ({
|
|
2467
|
-
kind: "branch",
|
|
2468
|
-
condition,
|
|
2469
|
-
arms: [blockStatements2(consequence), otherArm(node)]
|
|
2470
|
-
})
|
|
2906
|
+
const module = String(from[1]);
|
|
2907
|
+
return options.patterns.find(
|
|
2908
|
+
(pattern) => pattern.module === module && pattern.functions.includes(callee.text)
|
|
2471
2909
|
);
|
|
2472
2910
|
}
|
|
2473
|
-
function
|
|
2474
|
-
const
|
|
2475
|
-
const
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
);
|
|
2479
|
-
if (next === void 0) {
|
|
2480
|
-
return [];
|
|
2911
|
+
function statementIn(call) {
|
|
2912
|
+
const args = field(call, "arguments");
|
|
2913
|
+
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
2914
|
+
if (first === null || first.type !== "string") {
|
|
2915
|
+
return null;
|
|
2481
2916
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
}
|
|
2487
|
-
function blockStatements2(block) {
|
|
2488
|
-
return block === null ? [] : children(block);
|
|
2489
|
-
}
|
|
2490
|
-
function isRoot(node) {
|
|
2491
|
-
return ROOT_TYPES.has(node.type);
|
|
2492
|
-
}
|
|
2493
|
-
function isPathStatement(node, parent) {
|
|
2494
|
-
return parent.type === "module" || parent.type === "block" || node.type === "elif_clause";
|
|
2495
|
-
}
|
|
2496
|
-
function siteOf(node) {
|
|
2497
|
-
const path6 = [];
|
|
2498
|
-
let current = node;
|
|
2499
|
-
for (; ; ) {
|
|
2500
|
-
const parent = current.parent;
|
|
2501
|
-
if (parent === null) {
|
|
2502
|
-
return null;
|
|
2917
|
+
const parts = [""];
|
|
2918
|
+
for (const child of first.namedChildren) {
|
|
2919
|
+
if (child === null) {
|
|
2920
|
+
continue;
|
|
2503
2921
|
}
|
|
2504
|
-
if (
|
|
2505
|
-
|
|
2922
|
+
if (child.type === "string_content") {
|
|
2923
|
+
parts[parts.length - 1] += stringContentValue(child);
|
|
2924
|
+
continue;
|
|
2506
2925
|
}
|
|
2507
|
-
if (
|
|
2508
|
-
|
|
2926
|
+
if (child.type === "interpolation") {
|
|
2927
|
+
parts.push("");
|
|
2509
2928
|
}
|
|
2510
|
-
current = parent;
|
|
2511
2929
|
}
|
|
2930
|
+
const statement = sqlFromParts(parts);
|
|
2931
|
+
return statement.trim() === "" ? null : statement;
|
|
2512
2932
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2933
|
+
|
|
2934
|
+
// src/storage.ts
|
|
2935
|
+
import { storageBinding as storageBinding2 } from "@suss/ir-core";
|
|
2936
|
+
function receiverCall(call) {
|
|
2937
|
+
const callee = field(call, "function");
|
|
2938
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2939
|
+
return null;
|
|
2516
2940
|
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2941
|
+
const object = field(callee, "object");
|
|
2942
|
+
return object !== null && object.type === "call" ? object : null;
|
|
2943
|
+
}
|
|
2944
|
+
function methodName(call) {
|
|
2945
|
+
const callee = field(call, "function");
|
|
2946
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2947
|
+
return callee?.text ?? "";
|
|
2523
2948
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2949
|
+
return field(callee, "attribute")?.text ?? "";
|
|
2950
|
+
}
|
|
2951
|
+
function subjectOf(call) {
|
|
2952
|
+
const callee = field(call, "function");
|
|
2953
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2954
|
+
return callee?.text ?? "";
|
|
2526
2955
|
}
|
|
2527
|
-
|
|
2528
|
-
return {
|
|
2529
|
-
parameters: isMethod(node) ? parameters.slice(1) : parameters,
|
|
2530
|
-
body: blockStatements2(field(node, "body"))
|
|
2531
|
-
};
|
|
2956
|
+
return field(callee, "object")?.text ?? "";
|
|
2532
2957
|
}
|
|
2533
|
-
function
|
|
2534
|
-
const
|
|
2535
|
-
return
|
|
2958
|
+
function rootOf(call) {
|
|
2959
|
+
const receiver = receiverCall(call);
|
|
2960
|
+
return receiver === null ? call : rootOf(receiver);
|
|
2536
2961
|
}
|
|
2537
|
-
function
|
|
2538
|
-
const
|
|
2539
|
-
const
|
|
2540
|
-
const
|
|
2541
|
-
if (
|
|
2542
|
-
|
|
2962
|
+
function chainsIn(calls) {
|
|
2963
|
+
const endsWith = /* @__PURE__ */ new Map();
|
|
2964
|
+
for (const call of calls) {
|
|
2965
|
+
const receiver = receiverCall(call);
|
|
2966
|
+
if (receiver === null) {
|
|
2967
|
+
continue;
|
|
2543
2968
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2969
|
+
const start = rootOf(receiver);
|
|
2970
|
+
const kept = endsWith.get(start.id);
|
|
2971
|
+
if (kept === void 0 || call.endIndex > kept.endIndex) {
|
|
2972
|
+
endsWith.set(start.id, call);
|
|
2546
2973
|
}
|
|
2547
|
-
};
|
|
2548
|
-
for (const child of children(root)) {
|
|
2549
|
-
visit(child, isRoot(child));
|
|
2550
|
-
}
|
|
2551
|
-
return mutated;
|
|
2552
|
-
}
|
|
2553
|
-
function writtenNameOf(node, readingMethods) {
|
|
2554
|
-
if (node.type === "assignment" || node.type === "augmented_assignment") {
|
|
2555
|
-
const target = field(node, "left");
|
|
2556
|
-
return target?.type === "identifier" ? target.text : null;
|
|
2557
2974
|
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
if (
|
|
2561
|
-
|
|
2562
|
-
}
|
|
2563
|
-
const receiver = field(callee, "object");
|
|
2564
|
-
const method = field(callee, "attribute");
|
|
2565
|
-
if (receiver?.type === "identifier" && method !== null && !readingMethods.has(method.text)) {
|
|
2566
|
-
return receiver.text;
|
|
2975
|
+
const chains = [];
|
|
2976
|
+
for (const call of calls) {
|
|
2977
|
+
if (receiverCall(call) !== null) {
|
|
2978
|
+
continue;
|
|
2567
2979
|
}
|
|
2980
|
+
const last = endsWith.get(call.id) ?? call;
|
|
2981
|
+
chains.push({
|
|
2982
|
+
root: call,
|
|
2983
|
+
last,
|
|
2984
|
+
subject: subjectOf(call),
|
|
2985
|
+
operation: methodName(last),
|
|
2986
|
+
between: calls.filter(
|
|
2987
|
+
(other) => other !== call && other !== last && other.startIndex >= call.startIndex && other.endIndex <= last.endIndex
|
|
2988
|
+
)
|
|
2989
|
+
});
|
|
2568
2990
|
}
|
|
2569
|
-
return
|
|
2991
|
+
return chains;
|
|
2570
2992
|
}
|
|
2571
|
-
|
|
2572
|
-
"
|
|
2573
|
-
"
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
"
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
const read = /* @__PURE__ */ new Set();
|
|
2584
|
-
const visit = (node) => {
|
|
2585
|
-
for (const child of children(node)) {
|
|
2586
|
-
if (child.type !== "identifier") {
|
|
2587
|
-
visit(child);
|
|
2588
|
-
continue;
|
|
2589
|
-
}
|
|
2590
|
-
if (node.type === "attribute" && field(node, "attribute")?.id === child.id) {
|
|
2591
|
-
continue;
|
|
2592
|
-
}
|
|
2593
|
-
if (node.type === "keyword_argument" && field(node, "name")?.id === child.id) {
|
|
2594
|
-
continue;
|
|
2595
|
-
}
|
|
2596
|
-
if (DECLARING_PARENTS.has(node.type) || node.type === "assignment" && field(node, "left")?.id === child.id) {
|
|
2597
|
-
declared.add(child.text);
|
|
2598
|
-
continue;
|
|
2599
|
-
}
|
|
2600
|
-
read.add(child.text);
|
|
2601
|
-
}
|
|
2602
|
-
};
|
|
2603
|
-
visit(fn);
|
|
2604
|
-
return [...read].filter((name) => !declared.has(name));
|
|
2993
|
+
function importedQueryFunction(options, chain) {
|
|
2994
|
+
const callee = field(chain.root, "function");
|
|
2995
|
+
if (callee === null || callee.type !== "identifier") {
|
|
2996
|
+
return void 0;
|
|
2997
|
+
}
|
|
2998
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
2999
|
+
if (from === void 0) {
|
|
3000
|
+
return void 0;
|
|
3001
|
+
}
|
|
3002
|
+
return options.patterns.find(
|
|
3003
|
+
(pattern) => String(from[1]) === pattern.module && (pattern.queryFunctions ?? []).includes(String(from[2]))
|
|
3004
|
+
);
|
|
2605
3005
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
const chain = memberChainOf(callee);
|
|
2610
|
-
if (chain === null) {
|
|
3006
|
+
function receiverName(chain) {
|
|
3007
|
+
const callee = field(chain.root, "function");
|
|
3008
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2611
3009
|
return null;
|
|
2612
3010
|
}
|
|
2613
|
-
const
|
|
2614
|
-
|
|
2615
|
-
|
|
3011
|
+
const object = field(callee, "object");
|
|
3012
|
+
return object !== null && object.type === "identifier" ? object.text : null;
|
|
3013
|
+
}
|
|
3014
|
+
function typeNameOf(annotation) {
|
|
3015
|
+
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
3016
|
+
return typeNameOf(annotation.namedChildren[0]);
|
|
2616
3017
|
}
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
return rest.length === 0 ? { module: "builtins", name: rootName } : null;
|
|
3018
|
+
if (annotation.type === "identifier") {
|
|
3019
|
+
return annotation.text;
|
|
2620
3020
|
}
|
|
2621
|
-
const
|
|
2622
|
-
if (
|
|
2623
|
-
return
|
|
3021
|
+
const quoted = stringLiteralValue(annotation);
|
|
3022
|
+
if (quoted !== null) {
|
|
3023
|
+
return quoted;
|
|
2624
3024
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
3025
|
+
if (annotation.type === "generic_type") {
|
|
3026
|
+
const outer = annotation.namedChildren[0];
|
|
3027
|
+
const first = genericTypeArgs(annotation)[0];
|
|
3028
|
+
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
3029
|
+
return typeNameOf(first);
|
|
3030
|
+
}
|
|
2629
3031
|
}
|
|
2630
|
-
return
|
|
3032
|
+
return null;
|
|
2631
3033
|
}
|
|
2632
|
-
function
|
|
2633
|
-
if (
|
|
2634
|
-
if (
|
|
3034
|
+
function typeGivenBy(statement, name) {
|
|
3035
|
+
if (statement.type === "assignment") {
|
|
3036
|
+
if (field(statement, "left")?.text !== name) {
|
|
2635
3037
|
return null;
|
|
2636
3038
|
}
|
|
2637
|
-
|
|
3039
|
+
const annotation = field(statement, "type");
|
|
3040
|
+
if (annotation !== null) {
|
|
3041
|
+
return typeNameOf(annotation);
|
|
3042
|
+
}
|
|
3043
|
+
const right = field(statement, "right");
|
|
3044
|
+
const callee = right?.type === "call" ? field(right, "function") : null;
|
|
3045
|
+
return callee?.type === "identifier" ? callee.text : null;
|
|
2638
3046
|
}
|
|
2639
|
-
if (
|
|
2640
|
-
|
|
3047
|
+
if (statement.type === "as_pattern") {
|
|
3048
|
+
const alias = field(statement, "alias");
|
|
3049
|
+
const value = statement.namedChildren[0] ?? null;
|
|
3050
|
+
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
3051
|
+
return alias?.text === name && callee?.type === "identifier" ? callee.text : null;
|
|
2641
3052
|
}
|
|
2642
|
-
|
|
2643
|
-
return unaliasedDotted ? [localName] : binding.module.split(".");
|
|
3053
|
+
return null;
|
|
2644
3054
|
}
|
|
2645
|
-
function
|
|
2646
|
-
const
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
3055
|
+
function declaredTypeName(name, from) {
|
|
3056
|
+
const fn = enclosingFunction(from);
|
|
3057
|
+
if (fn === null) {
|
|
3058
|
+
return null;
|
|
3059
|
+
}
|
|
3060
|
+
const params = field(fn, "parameters");
|
|
3061
|
+
for (const param of params === null ? [] : children(params)) {
|
|
3062
|
+
const info = parameterNameAndType(param);
|
|
3063
|
+
if (info?.name === name && info.typeNode !== null) {
|
|
3064
|
+
return typeNameOf(info.typeNode);
|
|
2653
3065
|
}
|
|
2654
|
-
names.unshift(attribute.text);
|
|
2655
|
-
current = peelValue(object);
|
|
2656
3066
|
}
|
|
2657
|
-
|
|
3067
|
+
const visit = (node) => {
|
|
3068
|
+
const given = typeGivenBy(node, name);
|
|
3069
|
+
if (given !== null) {
|
|
3070
|
+
return given;
|
|
3071
|
+
}
|
|
3072
|
+
for (const child of children(node)) {
|
|
3073
|
+
if (child.type === "function_definition" || child.type === "lambda") {
|
|
3074
|
+
continue;
|
|
3075
|
+
}
|
|
3076
|
+
const found = visit(child);
|
|
3077
|
+
if (found !== null) {
|
|
3078
|
+
return found;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
2658
3081
|
return null;
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
const scope = module.scopeFor.get(current.id);
|
|
2666
|
-
if (scope !== void 0) {
|
|
2667
|
-
return scope;
|
|
3082
|
+
};
|
|
3083
|
+
const body = field(fn, "body");
|
|
3084
|
+
for (const statement of body === null ? [] : bodyStatements(body)) {
|
|
3085
|
+
const found = visit(statement);
|
|
3086
|
+
if (found !== null) {
|
|
3087
|
+
return found;
|
|
2668
3088
|
}
|
|
2669
|
-
current = current.parent;
|
|
2670
3089
|
}
|
|
2671
|
-
return
|
|
3090
|
+
return null;
|
|
2672
3091
|
}
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
concat,
|
|
2678
|
-
constant,
|
|
2679
|
-
environmentRead,
|
|
2680
|
-
equals,
|
|
2681
|
-
extended,
|
|
2682
|
-
fallback,
|
|
2683
|
-
formatArguments,
|
|
2684
|
-
hole,
|
|
2685
|
-
joined,
|
|
2686
|
-
joinedPath,
|
|
2687
|
-
literalOf,
|
|
2688
|
-
negated,
|
|
2689
|
-
operand,
|
|
2690
|
-
percentFormatted,
|
|
2691
|
-
plus,
|
|
2692
|
-
readableFallback,
|
|
2693
|
-
stripped,
|
|
2694
|
-
text,
|
|
2695
|
-
truthOf
|
|
2696
|
-
} from "@suss/values";
|
|
2697
|
-
var operatorRows = [
|
|
2698
|
-
{
|
|
2699
|
-
kind: "operator",
|
|
2700
|
-
operator: "+",
|
|
2701
|
-
arity: 2,
|
|
2702
|
-
apply: ([a, b]) => plus(operand(a), operand(b))
|
|
2703
|
-
},
|
|
2704
|
-
{
|
|
2705
|
-
kind: "operator",
|
|
2706
|
-
operator: "%",
|
|
2707
|
-
arity: 2,
|
|
2708
|
-
apply: ([a, b], contentOf) => percentFormatted(operand(a), formatArguments(contentOf(operand(b))))
|
|
2709
|
-
},
|
|
2710
|
-
{
|
|
2711
|
-
kind: "operator",
|
|
2712
|
-
operator: "==",
|
|
2713
|
-
arity: 2,
|
|
2714
|
-
apply: ([a, b]) => equals(operand(a), operand(b))
|
|
2715
|
-
},
|
|
2716
|
-
{
|
|
2717
|
-
kind: "operator",
|
|
2718
|
-
operator: "!=",
|
|
2719
|
-
arity: 2,
|
|
2720
|
-
apply: ([a, b]) => negated(equals(operand(a), operand(b)))
|
|
2721
|
-
},
|
|
2722
|
-
{
|
|
2723
|
-
kind: "operator",
|
|
2724
|
-
operator: "is",
|
|
2725
|
-
arity: 2,
|
|
2726
|
-
apply: ([a, b]) => equals(operand(a), operand(b))
|
|
2727
|
-
},
|
|
2728
|
-
{
|
|
2729
|
-
kind: "operator",
|
|
2730
|
-
operator: "is not",
|
|
2731
|
-
arity: 2,
|
|
2732
|
-
apply: ([a, b]) => negated(equals(operand(a), operand(b)))
|
|
2733
|
-
},
|
|
2734
|
-
{
|
|
2735
|
-
kind: "operator",
|
|
2736
|
-
operator: "not",
|
|
2737
|
-
arity: 1,
|
|
2738
|
-
apply: ([a]) => negated(operand(a))
|
|
2739
|
-
},
|
|
2740
|
-
{
|
|
2741
|
-
kind: "operator",
|
|
2742
|
-
operator: "or",
|
|
2743
|
-
arity: 2,
|
|
2744
|
-
apply: ([a, b]) => readableFallback(operand(a), operand(b), truthOf)
|
|
2745
|
-
},
|
|
2746
|
-
{
|
|
2747
|
-
kind: "operator",
|
|
2748
|
-
operator: "and",
|
|
2749
|
-
arity: 2,
|
|
2750
|
-
apply: ([a, b]) => fallback(operand(a), operand(b), (left) => {
|
|
2751
|
-
const truth = truthOf(left);
|
|
2752
|
-
return truth === null ? null : !truth;
|
|
2753
|
-
})
|
|
2754
|
-
}
|
|
2755
|
-
];
|
|
2756
|
-
var stringRows = [
|
|
2757
|
-
{
|
|
2758
|
-
kind: "method",
|
|
2759
|
-
method: "join",
|
|
2760
|
-
on: "string",
|
|
2761
|
-
apply: ({ receiver, args, contentOf }) => ({
|
|
2762
|
-
result: joined(contentOf(operand(args[0])), operand(receiver))
|
|
2763
|
-
})
|
|
2764
|
-
},
|
|
2765
|
-
{
|
|
2766
|
-
kind: "method",
|
|
2767
|
-
method: "format",
|
|
2768
|
-
on: "string",
|
|
2769
|
-
apply: ({ receiver, args }) => ({
|
|
2770
|
-
result: braceFormatted(operand(receiver), args)
|
|
2771
|
-
})
|
|
2772
|
-
},
|
|
2773
|
-
{
|
|
2774
|
-
kind: "method",
|
|
2775
|
-
method: "strip",
|
|
2776
|
-
on: "string",
|
|
2777
|
-
apply: ({ receiver, args }) => ({
|
|
2778
|
-
result: args.length === 0 ? stripped(operand(receiver)) : hole("value")
|
|
2779
|
-
})
|
|
2780
|
-
},
|
|
2781
|
-
{
|
|
2782
|
-
kind: "method",
|
|
2783
|
-
method: "rstrip",
|
|
2784
|
-
on: "string",
|
|
2785
|
-
apply: ({ receiver, args }) => ({
|
|
2786
|
-
result: args.length === 0 ? stripped(operand(receiver), "end") : hole("value")
|
|
2787
|
-
})
|
|
2788
|
-
},
|
|
2789
|
-
{
|
|
2790
|
-
kind: "method",
|
|
2791
|
-
method: "lstrip",
|
|
2792
|
-
on: "string",
|
|
2793
|
-
apply: ({ receiver, args }) => ({
|
|
2794
|
-
result: args.length === 0 ? stripped(operand(receiver), "start") : hole("value")
|
|
2795
|
-
})
|
|
2796
|
-
}
|
|
2797
|
-
];
|
|
2798
|
-
var sequenceRows = ["sequence", "unbounded"].flatMap((on) => [
|
|
2799
|
-
{
|
|
2800
|
-
kind: "method",
|
|
2801
|
-
method: "append",
|
|
2802
|
-
on,
|
|
2803
|
-
apply: ({ receiver, args }) => ({
|
|
2804
|
-
result: constant(null),
|
|
2805
|
-
receiver: appended(operand(receiver), args)
|
|
2806
|
-
})
|
|
2807
|
-
},
|
|
2808
|
-
{
|
|
2809
|
-
kind: "method",
|
|
2810
|
-
method: "extend",
|
|
2811
|
-
on,
|
|
2812
|
-
apply: ({ receiver, args, contentOf }) => ({
|
|
2813
|
-
result: constant(null),
|
|
2814
|
-
receiver: extended(operand(receiver), contentOf(operand(args[0])))
|
|
2815
|
-
})
|
|
3092
|
+
function typedReceiverPattern(options, chain) {
|
|
3093
|
+
const receiver = receiverName(chain);
|
|
3094
|
+
if (receiver === null) {
|
|
3095
|
+
return void 0;
|
|
2816
3096
|
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
kind: "callee",
|
|
2821
|
-
origin: { module: "builtins", name: "str" },
|
|
2822
|
-
apply: ({ args }) => ({ result: concat([operand(args[0])]) })
|
|
2823
|
-
},
|
|
2824
|
-
{
|
|
2825
|
-
kind: "callee",
|
|
2826
|
-
origin: { module: "os.path", name: "join" },
|
|
2827
|
-
apply: ({ args }) => ({ result: joinedPath(args) })
|
|
2828
|
-
},
|
|
2829
|
-
{
|
|
2830
|
-
kind: "callee",
|
|
2831
|
-
origin: { module: "os", name: "getenv" },
|
|
2832
|
-
apply: ({ args }) => ({ result: environmentRead(args) })
|
|
2833
|
-
},
|
|
2834
|
-
{
|
|
2835
|
-
kind: "callee",
|
|
2836
|
-
origin: { module: "os.environ", name: "get" },
|
|
2837
|
-
apply: ({ args }) => ({ result: environmentRead(args) })
|
|
3097
|
+
const typeName = declaredTypeName(receiver, chain.root);
|
|
3098
|
+
if (typeName === null) {
|
|
3099
|
+
return void 0;
|
|
2838
3100
|
}
|
|
2839
|
-
];
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
const template = literalOf(receiver);
|
|
2843
|
-
if (template === null) {
|
|
2844
|
-
return hole("value");
|
|
3101
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${typeName}`);
|
|
3102
|
+
if (from === void 0) {
|
|
3103
|
+
return void 0;
|
|
2845
3104
|
}
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
3105
|
+
return options.patterns.find(
|
|
3106
|
+
(pattern) => String(from[1]) === pattern.module && pattern.queryTypes.includes(String(from[2]))
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
function fieldsOf(chain, valueMethods) {
|
|
3110
|
+
const args = field(chain.root, "arguments");
|
|
3111
|
+
const named = [];
|
|
3112
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
3113
|
+
if (argument.type === "keyword_argument") {
|
|
3114
|
+
const name = field(argument, "name");
|
|
3115
|
+
if (name !== null) {
|
|
3116
|
+
named.push(name.text);
|
|
3117
|
+
}
|
|
3118
|
+
continue;
|
|
3119
|
+
}
|
|
3120
|
+
if (argument.type === "attribute") {
|
|
3121
|
+
const property = field(argument, "attribute");
|
|
3122
|
+
if (property !== null) {
|
|
3123
|
+
named.push(property.text);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
2860
3126
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
3127
|
+
for (const call of laterCalls(chain)) {
|
|
3128
|
+
if (valueMethods.includes(methodNameOf(call))) {
|
|
3129
|
+
named.push(...keywordNames(call));
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
return [...new Set(named)];
|
|
2863
3133
|
}
|
|
2864
|
-
|
|
2865
|
-
...
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
3134
|
+
function laterCalls(chain) {
|
|
3135
|
+
return [...chain.between, chain.last].filter((call) => call !== chain.root);
|
|
3136
|
+
}
|
|
3137
|
+
function methodNameOf(call) {
|
|
3138
|
+
const callee = field(call, "function");
|
|
3139
|
+
return callee?.type === "attribute" ? field(callee, "attribute")?.text ?? "" : callee?.text ?? "";
|
|
3140
|
+
}
|
|
3141
|
+
function keywordNames(call) {
|
|
3142
|
+
const args = field(call, "arguments");
|
|
3143
|
+
const picked = [];
|
|
3144
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
3145
|
+
if (argument.type === "keyword_argument") {
|
|
3146
|
+
const name = field(argument, "name");
|
|
3147
|
+
if (name !== null) {
|
|
3148
|
+
picked.push(name.text);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
2878
3151
|
}
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
rows: pythonRows
|
|
2886
|
-
})
|
|
3152
|
+
return picked;
|
|
3153
|
+
}
|
|
3154
|
+
function selectorOf(chain, valueMethods) {
|
|
3155
|
+
return [
|
|
3156
|
+
...new Set(
|
|
3157
|
+
laterCalls(chain).filter((call) => !valueMethods.includes(methodNameOf(call))).flatMap(keywordNames)
|
|
2887
3158
|
)
|
|
2888
|
-
|
|
3159
|
+
];
|
|
2889
3160
|
}
|
|
2890
|
-
function
|
|
2891
|
-
|
|
3161
|
+
function effectFor(pattern, chain, operation) {
|
|
3162
|
+
const valueMethods = pattern.valueMethods ?? [];
|
|
3163
|
+
const picked = selectorOf(chain, valueMethods);
|
|
3164
|
+
return {
|
|
3165
|
+
type: "interaction",
|
|
3166
|
+
binding: storageBinding2({
|
|
3167
|
+
recognition: "python-storage",
|
|
3168
|
+
storageSystem: pattern.storageSystem,
|
|
3169
|
+
scope: pattern.module,
|
|
3170
|
+
container: chain.subject
|
|
3171
|
+
}),
|
|
3172
|
+
callee: chain.last.text,
|
|
3173
|
+
interaction: {
|
|
3174
|
+
class: "storage-access",
|
|
3175
|
+
kind: pattern.writes.includes(operation) ? "write" : "read",
|
|
3176
|
+
fields: fieldsOf(chain, valueMethods),
|
|
3177
|
+
operation,
|
|
3178
|
+
...picked.length > 0 ? { selector: picked } : {}
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
2892
3181
|
}
|
|
2893
|
-
function
|
|
2894
|
-
|
|
3182
|
+
function fileOf(key) {
|
|
3183
|
+
const at = key.lastIndexOf(":");
|
|
3184
|
+
return at === -1 ? key : key.slice(0, at);
|
|
2895
3185
|
}
|
|
2896
|
-
function
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
return bound;
|
|
3186
|
+
function returnTypeName(node) {
|
|
3187
|
+
if (node === void 0) {
|
|
3188
|
+
return null;
|
|
2900
3189
|
}
|
|
2901
|
-
const
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
local = new Evaluator(
|
|
2905
|
-
pythonLowering({
|
|
2906
|
-
context: null,
|
|
2907
|
-
originOf: calleeOrigin,
|
|
2908
|
-
rows: pythonRows
|
|
2909
|
-
})
|
|
2910
|
-
);
|
|
2911
|
-
withoutFacts.set(tree, local);
|
|
3190
|
+
const annotation = field(node, "return_type");
|
|
3191
|
+
if (annotation === null) {
|
|
3192
|
+
return null;
|
|
2912
3193
|
}
|
|
2913
|
-
return
|
|
3194
|
+
return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
|
|
2914
3195
|
}
|
|
2915
|
-
function
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3196
|
+
function storageCallees(calls, filePath, couldMatch) {
|
|
3197
|
+
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)));
|
|
3198
|
+
}
|
|
3199
|
+
function resolvedMethodPattern(options, callee) {
|
|
3200
|
+
const resolved = options.facts.facts("wantedResolves").filter(
|
|
3201
|
+
(row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
|
|
3202
|
+
).map((row) => String(row[1]));
|
|
3203
|
+
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
3204
|
+
if (settled === void 0) {
|
|
3205
|
+
return void 0;
|
|
2921
3206
|
}
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3207
|
+
const typeName = returnTypeName(options.definitionAt(settled));
|
|
3208
|
+
if (typeName === null) {
|
|
3209
|
+
return void 0;
|
|
3210
|
+
}
|
|
3211
|
+
return options.patterns.find(
|
|
3212
|
+
(candidate) => candidate.queryTypes.includes(typeName) && options.facts.facts("pyImport").some(
|
|
3213
|
+
(row) => String(row[0]) === fileOf(settled) && String(row[1]) === candidate.module
|
|
3214
|
+
)
|
|
3215
|
+
);
|
|
2926
3216
|
}
|
|
2927
|
-
function
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
for (const entry of nodes.files) {
|
|
2931
|
-
filesByRoot.set(entry.root.id, entry);
|
|
2932
|
-
rootsByFile.set(entry.file, entry.root);
|
|
3217
|
+
function matchedChains(calls, options) {
|
|
3218
|
+
if (options.patterns.length === 0) {
|
|
3219
|
+
return [];
|
|
2933
3220
|
}
|
|
2934
|
-
const
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
3221
|
+
const named = new Set(
|
|
3222
|
+
options.patterns.flatMap((pattern) => pattern.queryFunctions ?? [])
|
|
3223
|
+
);
|
|
3224
|
+
const chains = chainsIn(calls).filter(
|
|
3225
|
+
(chain) => options.couldMatch.has(methodName(chain.root)) || named.has(methodName(chain.root)) || typedReceiverPattern(options, chain) !== void 0
|
|
3226
|
+
);
|
|
3227
|
+
if (chains.length === 0) {
|
|
3228
|
+
return [];
|
|
3229
|
+
}
|
|
3230
|
+
const alreadyAsked = new Set(
|
|
3231
|
+
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
3232
|
+
);
|
|
3233
|
+
resolveCalls(
|
|
3234
|
+
options.facts,
|
|
3235
|
+
storageCallees(calls, options.filePath, options.couldMatch).filter(
|
|
3236
|
+
(key) => !alreadyAsked.has(key)
|
|
3237
|
+
)
|
|
3238
|
+
);
|
|
3239
|
+
const matched = [];
|
|
3240
|
+
for (const chain of chains) {
|
|
3241
|
+
const callee = field(chain.root, "function");
|
|
3242
|
+
if (callee === null) {
|
|
3243
|
+
continue;
|
|
2938
3244
|
}
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
const answer = writtenValueOf(db, key);
|
|
2948
|
-
return answer === null ? null : nodeOfKey(rootsByFile, answer);
|
|
2949
|
-
},
|
|
2950
|
-
callable: (call) => {
|
|
2951
|
-
const callee = field(call, "function");
|
|
2952
|
-
const key = callee === null ? null : keyOf2(callee);
|
|
2953
|
-
if (key === null) {
|
|
2954
|
-
return null;
|
|
2955
|
-
}
|
|
2956
|
-
resolveCalls(db, [key]);
|
|
2957
|
-
const resolved = db.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
2958
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2959
|
-
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
3245
|
+
const imported = importedQueryFunction(options, chain);
|
|
3246
|
+
if (imported !== void 0) {
|
|
3247
|
+
matched.push({
|
|
3248
|
+
chain,
|
|
3249
|
+
pattern: imported,
|
|
3250
|
+
operation: methodName(chain.root)
|
|
3251
|
+
});
|
|
3252
|
+
continue;
|
|
2960
3253
|
}
|
|
2961
|
-
|
|
3254
|
+
const typed = typedReceiverPattern(options, chain);
|
|
3255
|
+
if (typed !== void 0) {
|
|
3256
|
+
const silent = (typed.recordsNothing ?? []).includes(
|
|
3257
|
+
methodName(chain.root)
|
|
3258
|
+
);
|
|
3259
|
+
matched.push({
|
|
3260
|
+
chain,
|
|
3261
|
+
pattern: typed,
|
|
3262
|
+
operation: silent ? null : chain.operation
|
|
3263
|
+
});
|
|
3264
|
+
continue;
|
|
3265
|
+
}
|
|
3266
|
+
const pattern = resolvedMethodPattern(options, callee);
|
|
3267
|
+
if (pattern !== void 0) {
|
|
3268
|
+
matched.push({ chain, pattern, operation: chain.operation });
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
return matched;
|
|
3272
|
+
}
|
|
3273
|
+
function storageCallIds(calls, options) {
|
|
3274
|
+
return new Set(
|
|
3275
|
+
matchedChains(calls, options).map(({ chain }) => chain.root.id)
|
|
3276
|
+
);
|
|
3277
|
+
}
|
|
3278
|
+
function storageEffects(calls, options) {
|
|
3279
|
+
return matchedChains(calls, options).flatMap(
|
|
3280
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
|
|
3281
|
+
);
|
|
2962
3282
|
}
|
|
2963
3283
|
|
|
2964
3284
|
// src/discovery.ts
|
|
@@ -3223,6 +3543,8 @@ function classRouteUnits(pattern, pack, classification, classNode, module, optio
|
|
|
3223
3543
|
...entry.mount !== void 0 ? { mount: entry.mount } : {},
|
|
3224
3544
|
requestBodyFromAnnotatedClass: pattern.annotatedClassIsRequestBody === true,
|
|
3225
3545
|
readsReturnedStatus: pattern.statusFromReturnedTuple === true,
|
|
3546
|
+
responseStatusCalls: pattern.responseStatusCalls ?? [],
|
|
3547
|
+
facts: options.facts,
|
|
3226
3548
|
injectedCallees: new Set(pattern.injectedParameterCallees ?? []),
|
|
3227
3549
|
definitionNode: maybeMethod,
|
|
3228
3550
|
enclosingScope: classScope,
|
|
@@ -3407,36 +3729,106 @@ function effectsReaching(effects, conditions) {
|
|
|
3407
3729
|
(effect) => guardsHoldOn(effect.preconditions, conditions)
|
|
3408
3730
|
);
|
|
3409
3731
|
}
|
|
3410
|
-
function
|
|
3411
|
-
if (!readsReturnedStatus) {
|
|
3412
|
-
return
|
|
3732
|
+
function returnStatusReading(statement, ctx) {
|
|
3733
|
+
if (!ctx.readsReturnedStatus) {
|
|
3734
|
+
return ctx.declaredStatus.reading;
|
|
3413
3735
|
}
|
|
3414
|
-
const
|
|
3415
|
-
const
|
|
3416
|
-
if (
|
|
3736
|
+
const read = statusOfReturn(statement);
|
|
3737
|
+
const range = rangeOf(statement);
|
|
3738
|
+
if (read.kind === "status") {
|
|
3739
|
+
return writtenReading(read.value, range);
|
|
3740
|
+
}
|
|
3741
|
+
if (read.kind === "none") {
|
|
3742
|
+
return absentReading;
|
|
3743
|
+
}
|
|
3744
|
+
return unreadableReading(
|
|
3745
|
+
"This return gives a status this reading cannot resolve to a number, so this outcome claims none",
|
|
3746
|
+
range
|
|
3747
|
+
);
|
|
3748
|
+
}
|
|
3749
|
+
function returnBranch(statement, ctx) {
|
|
3750
|
+
const range = rangeOf(statement);
|
|
3751
|
+
const writtenBody = returnedBodyShape(statement);
|
|
3752
|
+
return {
|
|
3753
|
+
terminal: {
|
|
3754
|
+
kind: "response",
|
|
3755
|
+
// These stay null here. The extractor fills them in from the two
|
|
3756
|
+
// readings below.
|
|
3757
|
+
statusCode: null,
|
|
3758
|
+
body: { typeText: null, shape: null },
|
|
3759
|
+
exceptionType: null,
|
|
3760
|
+
message: null,
|
|
3761
|
+
component: null,
|
|
3762
|
+
renderTree: null,
|
|
3763
|
+
delegateTarget: null,
|
|
3764
|
+
emitEvent: null,
|
|
3765
|
+
location: range
|
|
3766
|
+
},
|
|
3767
|
+
statusCodeReading: {
|
|
3768
|
+
reading: returnStatusReading(statement, ctx),
|
|
3769
|
+
...ctx.declaredStatus.libraryDefault !== void 0 ? { libraryDefault: ctx.declaredStatus.libraryDefault } : {}
|
|
3770
|
+
},
|
|
3771
|
+
bodyShapeReading: {
|
|
3772
|
+
reading: writtenBody === null ? ctx.responseShape.reading : writtenReading(writtenBody, range)
|
|
3773
|
+
},
|
|
3774
|
+
location: range
|
|
3775
|
+
};
|
|
3776
|
+
}
|
|
3777
|
+
function terminalBranchOf(found, ctx) {
|
|
3778
|
+
const table = {
|
|
3779
|
+
return: (variant) => returnBranch(variant.statement, ctx),
|
|
3780
|
+
raise: (variant) => ({
|
|
3781
|
+
terminal: variant.terminal,
|
|
3782
|
+
location: rangeOf(variant.statement)
|
|
3783
|
+
})
|
|
3784
|
+
};
|
|
3785
|
+
return dispatchByType(table, found);
|
|
3786
|
+
}
|
|
3787
|
+
function routeTerminals(body, raised) {
|
|
3788
|
+
const found = [
|
|
3789
|
+
...returnStatements(body).map(
|
|
3790
|
+
(statement) => ({ type: "return", statement })
|
|
3791
|
+
),
|
|
3792
|
+
...raised.map(
|
|
3793
|
+
(response) => ({
|
|
3794
|
+
type: "raise",
|
|
3795
|
+
statement: response.statement,
|
|
3796
|
+
terminal: response.terminal
|
|
3797
|
+
})
|
|
3798
|
+
)
|
|
3799
|
+
];
|
|
3800
|
+
return found.sort((a, b) => a.statement.startIndex - b.statement.startIndex);
|
|
3801
|
+
}
|
|
3802
|
+
function branchesPerTerminal(options) {
|
|
3803
|
+
const body = field(options.definitionNode, "body");
|
|
3804
|
+
const raised = raisedResponses(body, {
|
|
3805
|
+
calls: options.responseStatusCalls,
|
|
3806
|
+
module: options.module,
|
|
3807
|
+
facts: options.facts
|
|
3808
|
+
});
|
|
3809
|
+
const terminals = routeTerminals(body, raised);
|
|
3810
|
+
if (terminals.length < 2) {
|
|
3417
3811
|
return null;
|
|
3418
3812
|
}
|
|
3419
|
-
const
|
|
3813
|
+
const statements = terminals.map((found) => found.statement);
|
|
3814
|
+
const thrownByCall = new NodeSet(
|
|
3815
|
+
raised.filter((response) => response.thrownByCall).map((response) => response.statement)
|
|
3816
|
+
);
|
|
3817
|
+
const lowered = lowerPythonBody(body, statements, thrownByCall);
|
|
3420
3818
|
const enumerated = enumerateOrDegrade2(
|
|
3421
3819
|
{
|
|
3422
3820
|
statements: lowered.statements,
|
|
3423
3821
|
terminalsByStmt: lowered.terminalsByStmt
|
|
3424
3822
|
},
|
|
3425
|
-
|
|
3823
|
+
statements
|
|
3426
3824
|
);
|
|
3427
3825
|
const branches = [];
|
|
3428
|
-
for (const
|
|
3429
|
-
const paths = enumerated.byTerminal.get(statement);
|
|
3826
|
+
for (const found of terminals) {
|
|
3827
|
+
const paths = enumerated.byTerminal.get(found.statement);
|
|
3430
3828
|
if (paths === void 0) {
|
|
3431
3829
|
continue;
|
|
3432
3830
|
}
|
|
3433
|
-
const
|
|
3434
|
-
const range = rangeOf(statement);
|
|
3435
|
-
const writtenBody = returnedBodyShape(statement);
|
|
3436
|
-
const statusReading = read.kind === "status" ? writtenReading(read.value, range) : read.kind === "none" ? absentReading : unreadableReading(
|
|
3437
|
-
"This return gives a status this reading cannot resolve to a number, so this outcome claims none",
|
|
3438
|
-
range
|
|
3439
|
-
);
|
|
3831
|
+
const branch = terminalBranchOf(found, options.ctx);
|
|
3440
3832
|
for (const path6 of paths) {
|
|
3441
3833
|
const conditions = path6.map((condition) => ({
|
|
3442
3834
|
sourceText: condition.sourceText,
|
|
@@ -3445,28 +3837,9 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
|
|
|
3445
3837
|
source: condition.source
|
|
3446
3838
|
}));
|
|
3447
3839
|
branches.push({
|
|
3840
|
+
...branch,
|
|
3448
3841
|
conditions,
|
|
3449
|
-
|
|
3450
|
-
kind: "response",
|
|
3451
|
-
statusCode: null,
|
|
3452
|
-
body: { typeText: null, shape: null },
|
|
3453
|
-
exceptionType: null,
|
|
3454
|
-
message: null,
|
|
3455
|
-
component: null,
|
|
3456
|
-
renderTree: null,
|
|
3457
|
-
delegateTarget: null,
|
|
3458
|
-
emitEvent: null,
|
|
3459
|
-
location: range
|
|
3460
|
-
},
|
|
3461
|
-
statusCodeReading: {
|
|
3462
|
-
reading: statusReading,
|
|
3463
|
-
...declaredStatus.libraryDefault !== void 0 ? { libraryDefault: declaredStatus.libraryDefault } : {}
|
|
3464
|
-
},
|
|
3465
|
-
bodyShapeReading: {
|
|
3466
|
-
reading: writtenBody === null ? responseShape.reading : writtenReading(writtenBody, range)
|
|
3467
|
-
},
|
|
3468
|
-
effects: effectsReaching(effects, conditions),
|
|
3469
|
-
location: range,
|
|
3842
|
+
effects: effectsReaching(options.effects, conditions),
|
|
3470
3843
|
isDefault: path6.length === 0
|
|
3471
3844
|
});
|
|
3472
3845
|
}
|
|
@@ -3571,6 +3944,8 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
3571
3944
|
...entry.mount !== void 0 ? { mount: entry.mount } : {},
|
|
3572
3945
|
requestBodyFromAnnotatedClass: pattern.annotatedClassIsRequestBody === true,
|
|
3573
3946
|
readsReturnedStatus: pattern.statusFromReturnedTuple === true,
|
|
3947
|
+
responseStatusCalls: pattern.responseStatusCalls ?? [],
|
|
3948
|
+
facts: options.facts,
|
|
3574
3949
|
injectedCallees: new Set(pattern.injectedParameterCallees ?? []),
|
|
3575
3950
|
definitionNode: functionNode,
|
|
3576
3951
|
enclosingScope: module.moduleScope,
|
|
@@ -3679,17 +4054,18 @@ function buildRouteUnit(options) {
|
|
|
3679
4054
|
);
|
|
3680
4055
|
const effects = invocationEffects(definitionNode);
|
|
3681
4056
|
const extra = recognizedBodyEffects(definitionNode, module, storageLookup);
|
|
3682
|
-
const
|
|
3683
|
-
readsReturnedStatus,
|
|
4057
|
+
const perTerminal = branchesPerTerminal({
|
|
3684
4058
|
definitionNode,
|
|
3685
4059
|
effects,
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
4060
|
+
module,
|
|
4061
|
+
facts: options.facts,
|
|
4062
|
+
responseStatusCalls: options.responseStatusCalls,
|
|
4063
|
+
ctx: { readsReturnedStatus, responseShape, declaredStatus: statusCode }
|
|
4064
|
+
});
|
|
4065
|
+
const branches = (perTerminal ?? []).map(
|
|
3690
4066
|
(branch) => extra.length === 0 ? branch : { ...branch, extraEffects: extra }
|
|
3691
4067
|
);
|
|
3692
|
-
if (
|
|
4068
|
+
if (perTerminal === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" || extra.length > 0 || effects.length > 0)) {
|
|
3693
4069
|
branches.push({
|
|
3694
4070
|
conditions: [],
|
|
3695
4071
|
terminal: {
|
|
@@ -3771,6 +4147,15 @@ function recognizedBodyEffects(definitionNode, module, storageLookup) {
|
|
|
3771
4147
|
];
|
|
3772
4148
|
return [...envReadEffects(definitionNode, module), ...storage];
|
|
3773
4149
|
}
|
|
4150
|
+
function recognizedCallIds(definitionNode, storageLookup) {
|
|
4151
|
+
if (storageLookup === void 0) {
|
|
4152
|
+
return /* @__PURE__ */ new Set();
|
|
4153
|
+
}
|
|
4154
|
+
return storageCallIds(bodyCalls(definitionNode), {
|
|
4155
|
+
...storageLookup,
|
|
4156
|
+
filePath: storageLookup.factsPath
|
|
4157
|
+
});
|
|
4158
|
+
}
|
|
3774
4159
|
function bodyContentOf(bodyNode) {
|
|
3775
4160
|
for (const stmt of bodyStatements(bodyNode)) {
|
|
3776
4161
|
if (stmt.type === "pass_statement") {
|
|
@@ -3885,26 +4270,6 @@ function isInjectedParameter(param, injectedCallees) {
|
|
|
3885
4270
|
};
|
|
3886
4271
|
return containsInjectorCall(field(param, "type"), 0);
|
|
3887
4272
|
}
|
|
3888
|
-
function parameterNameAndType(param) {
|
|
3889
|
-
if (param.type === "identifier") {
|
|
3890
|
-
return { name: param.text, typeNode: null };
|
|
3891
|
-
}
|
|
3892
|
-
if (param.type === "typed_parameter") {
|
|
3893
|
-
const inner = param.namedChildren.find(
|
|
3894
|
-
(child) => child !== null && child.type === "identifier"
|
|
3895
|
-
);
|
|
3896
|
-
return inner !== void 0 ? { name: inner.text, typeNode: field(param, "type") } : null;
|
|
3897
|
-
}
|
|
3898
|
-
if (param.type === "default_parameter") {
|
|
3899
|
-
const nameNode = field(param, "name");
|
|
3900
|
-
return nameNode?.type === "identifier" ? { name: nameNode.text, typeNode: null } : null;
|
|
3901
|
-
}
|
|
3902
|
-
if (param.type === "typed_default_parameter") {
|
|
3903
|
-
const nameNode = field(param, "name");
|
|
3904
|
-
return nameNode !== null ? { name: nameNode.text, typeNode: field(param, "type") } : null;
|
|
3905
|
-
}
|
|
3906
|
-
return null;
|
|
3907
|
-
}
|
|
3908
4273
|
|
|
3909
4274
|
// src/facts/why.ts
|
|
3910
4275
|
import fs4 from "fs";
|
|
@@ -4449,7 +4814,11 @@ function reachedFunctions(seeds, options) {
|
|
|
4449
4814
|
if (source === void 0) {
|
|
4450
4815
|
continue;
|
|
4451
4816
|
}
|
|
4452
|
-
const scan = scanBody(
|
|
4817
|
+
const scan = scanBody(
|
|
4818
|
+
source,
|
|
4819
|
+
ctx,
|
|
4820
|
+
recognizedCallIds(source.node, options.storageFor(source.file))
|
|
4821
|
+
);
|
|
4453
4822
|
if (scan.stops.length > 0) {
|
|
4454
4823
|
stopsByKey.set(key, scan.stops);
|
|
4455
4824
|
}
|
|
@@ -4514,7 +4883,7 @@ var EMPTY_SCAN = {
|
|
|
4514
4883
|
parameterCalls: [],
|
|
4515
4884
|
passedPositions: /* @__PURE__ */ new Set()
|
|
4516
4885
|
};
|
|
4517
|
-
function scanBody(source, ctx) {
|
|
4886
|
+
function scanBody(source, ctx, recognized) {
|
|
4518
4887
|
const followed = [];
|
|
4519
4888
|
const stops = [];
|
|
4520
4889
|
const placements = new TargetPlacements();
|
|
@@ -4579,7 +4948,7 @@ function scanBody(source, ctx) {
|
|
|
4579
4948
|
);
|
|
4580
4949
|
if (outcome.kind === "stopped") {
|
|
4581
4950
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
4582
|
-
if (!seen.has(stopKey) && worthRecording(outcome.reason)) {
|
|
4951
|
+
if (!seen.has(stopKey) && !recognized.has(call.id) && worthRecording(outcome.reason)) {
|
|
4583
4952
|
seen.add(stopKey);
|
|
4584
4953
|
stops.push({ callee, reason: outcome.reason });
|
|
4585
4954
|
}
|