@suss/adapter-python 0.22.2 → 0.22.4
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 +14 -12
package/dist/index.cjs
CHANGED
|
@@ -100,6 +100,40 @@ function enclosingFunction(node) {
|
|
|
100
100
|
}
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
|
+
function stringContentValue(content) {
|
|
104
|
+
const base = content.startIndex;
|
|
105
|
+
let text2 = "";
|
|
106
|
+
let cursor = 0;
|
|
107
|
+
for (const child of children(content)) {
|
|
108
|
+
if (child.type !== "escape_interpolation") {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
text2 += content.text.slice(cursor, child.startIndex - base);
|
|
112
|
+
text2 += child.text.slice(0, 1);
|
|
113
|
+
cursor = child.endIndex - base;
|
|
114
|
+
}
|
|
115
|
+
return text2 + content.text.slice(cursor);
|
|
116
|
+
}
|
|
117
|
+
function parameterNameAndType(param) {
|
|
118
|
+
if (param.type === "identifier") {
|
|
119
|
+
return { name: param.text, typeNode: null };
|
|
120
|
+
}
|
|
121
|
+
if (param.type === "typed_parameter") {
|
|
122
|
+
const inner = param.namedChildren.find(
|
|
123
|
+
(child) => child !== null && child.type === "identifier"
|
|
124
|
+
);
|
|
125
|
+
return inner !== void 0 ? { name: inner.text, typeNode: field(param, "type") } : null;
|
|
126
|
+
}
|
|
127
|
+
if (param.type === "default_parameter") {
|
|
128
|
+
const nameNode = field(param, "name");
|
|
129
|
+
return nameNode?.type === "identifier" ? { name: nameNode.text, typeNode: null } : null;
|
|
130
|
+
}
|
|
131
|
+
if (param.type === "typed_default_parameter") {
|
|
132
|
+
const nameNode = field(param, "name");
|
|
133
|
+
return nameNode !== null ? { name: nameNode.text, typeNode: field(param, "type") } : null;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
103
137
|
function stringLiteralValue(node) {
|
|
104
138
|
if (node.type !== "string") {
|
|
105
139
|
return null;
|
|
@@ -110,7 +144,7 @@ function stringLiteralValue(node) {
|
|
|
110
144
|
continue;
|
|
111
145
|
}
|
|
112
146
|
if (child.type === "string_content") {
|
|
113
|
-
content += child
|
|
147
|
+
content += stringContentValue(child);
|
|
114
148
|
continue;
|
|
115
149
|
}
|
|
116
150
|
if (child.type === "interpolation") {
|
|
@@ -138,6 +172,8 @@ function stripDecorators(node) {
|
|
|
138
172
|
);
|
|
139
173
|
return { definition, decorators };
|
|
140
174
|
}
|
|
175
|
+
var NodeSet = class extends import_extractor.IdSet {
|
|
176
|
+
};
|
|
141
177
|
var NodeMap = class extends import_extractor.IdMap {
|
|
142
178
|
};
|
|
143
179
|
|
|
@@ -835,7 +871,7 @@ function passesParametersThrough(def, inner) {
|
|
|
835
871
|
// src/discovery.ts
|
|
836
872
|
var import_behavioral_ir2 = require("@suss/behavioral-ir");
|
|
837
873
|
var import_extractor4 = require("@suss/extractor");
|
|
838
|
-
var
|
|
874
|
+
var import_values7 = require("@suss/values");
|
|
839
875
|
|
|
840
876
|
// src/envReads.ts
|
|
841
877
|
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
@@ -1465,14 +1501,14 @@ var EXIT_KINDS = {
|
|
|
1465
1501
|
continue_statement: "continue"
|
|
1466
1502
|
};
|
|
1467
1503
|
var NESTED_FUNCTION_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
1468
|
-
function exitKindOf(node) {
|
|
1504
|
+
function exitKindOf(node, thrown) {
|
|
1469
1505
|
let sawReturn = false;
|
|
1470
1506
|
const raises = (current) => {
|
|
1471
1507
|
for (const child of current.namedChildren) {
|
|
1472
1508
|
if (child === null || NESTED_FUNCTION_TYPES.has(child.type)) {
|
|
1473
1509
|
continue;
|
|
1474
1510
|
}
|
|
1475
|
-
if (child.type === "raise_statement") {
|
|
1511
|
+
if (child.type === "raise_statement" || thrown.has(child)) {
|
|
1476
1512
|
return true;
|
|
1477
1513
|
}
|
|
1478
1514
|
if (child.type === "return_statement") {
|
|
@@ -1506,10 +1542,12 @@ function childBlock(node) {
|
|
|
1506
1542
|
var Lowerer = class {
|
|
1507
1543
|
// tree-sitter hands back a fresh wrapper object each time a child is
|
|
1508
1544
|
// read, so a terminal is matched by its stable node id.
|
|
1509
|
-
constructor(terminals) {
|
|
1545
|
+
constructor(terminals, thrown) {
|
|
1510
1546
|
this.terminals = terminals;
|
|
1547
|
+
this.thrown = thrown;
|
|
1511
1548
|
}
|
|
1512
1549
|
terminals;
|
|
1550
|
+
thrown;
|
|
1513
1551
|
terminalsByStmt = /* @__PURE__ */ new Map();
|
|
1514
1552
|
terminalHome = new NodeMap();
|
|
1515
1553
|
attach(statement, candidates) {
|
|
@@ -1542,7 +1580,7 @@ var Lowerer = class {
|
|
|
1542
1580
|
condition: handleOf(field(clause, "condition")),
|
|
1543
1581
|
thenBody: this.lowerBlock(field(clause, "consequence")),
|
|
1544
1582
|
elseBody: this.lowerIfTail(clauses, index + 1),
|
|
1545
|
-
exitKind: exitKindOf(clause)
|
|
1583
|
+
exitKind: exitKindOf(clause, this.thrown)
|
|
1546
1584
|
};
|
|
1547
1585
|
this.attach(nested, [field(clause, "condition")]);
|
|
1548
1586
|
return [nested];
|
|
@@ -1556,7 +1594,7 @@ var Lowerer = class {
|
|
|
1556
1594
|
condition: handleOf(field(node, "condition")),
|
|
1557
1595
|
thenBody: this.lowerBlock(field(node, "consequence")),
|
|
1558
1596
|
elseBody: this.lowerIfTail(clauses, 0),
|
|
1559
|
-
exitKind: exitKindOf(node)
|
|
1597
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1560
1598
|
};
|
|
1561
1599
|
}
|
|
1562
1600
|
lowerTry(node) {
|
|
@@ -1571,7 +1609,7 @@ var Lowerer = class {
|
|
|
1571
1609
|
tryBody: this.lowerBlock(field(node, "body")),
|
|
1572
1610
|
catchBody: excepts.length > 0 ? excepts.flatMap((clause) => this.lowerBlock(childBlock(clause))) : null,
|
|
1573
1611
|
finallyBody: finallyClause === null ? null : this.lowerBlock(childBlock(finallyClause)),
|
|
1574
|
-
exitKind: exitKindOf(node)
|
|
1612
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1575
1613
|
};
|
|
1576
1614
|
}
|
|
1577
1615
|
lowerMatch(node) {
|
|
@@ -1588,15 +1626,21 @@ var Lowerer = class {
|
|
|
1588
1626
|
body: this.lowerBlock(field(clause, "consequence"))
|
|
1589
1627
|
};
|
|
1590
1628
|
});
|
|
1591
|
-
return { kind: "switch", groups, exitKind: exitKindOf(node) };
|
|
1629
|
+
return { kind: "switch", groups, exitKind: exitKindOf(node, this.thrown) };
|
|
1592
1630
|
}
|
|
1593
1631
|
lower(node) {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
return this.attach({ kind: "exit", exit, exitKind: exitKindOf(node) }, [
|
|
1632
|
+
if (this.thrown.has(node)) {
|
|
1633
|
+
return this.attach({ kind: "exit", exit: "throw", exitKind: "throw" }, [
|
|
1597
1634
|
node
|
|
1598
1635
|
]);
|
|
1599
1636
|
}
|
|
1637
|
+
const exit = EXIT_KINDS[node.type];
|
|
1638
|
+
if (exit !== void 0) {
|
|
1639
|
+
return this.attach(
|
|
1640
|
+
{ kind: "exit", exit, exitKind: exitKindOf(node, this.thrown) },
|
|
1641
|
+
[node]
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1600
1644
|
if (node.type === "if_statement") {
|
|
1601
1645
|
return this.attach(this.lowerIf(node), [field(node, "condition")]);
|
|
1602
1646
|
}
|
|
@@ -1607,7 +1651,7 @@ var Lowerer = class {
|
|
|
1607
1651
|
kind: "loop",
|
|
1608
1652
|
condition: handleOf(header),
|
|
1609
1653
|
body: this.lowerBlock(field(node, "body")),
|
|
1610
|
-
exitKind: exitKindOf(node)
|
|
1654
|
+
exitKind: exitKindOf(node, this.thrown)
|
|
1611
1655
|
},
|
|
1612
1656
|
[header]
|
|
1613
1657
|
);
|
|
@@ -1618,12 +1662,16 @@ var Lowerer = class {
|
|
|
1618
1662
|
if (node.type === "match_statement") {
|
|
1619
1663
|
return this.attach(this.lowerMatch(node), [field(node, "subject")]);
|
|
1620
1664
|
}
|
|
1621
|
-
return this.attach(
|
|
1665
|
+
return this.attach(
|
|
1666
|
+
{ kind: "opaque", exitKind: exitKindOf(node, this.thrown) },
|
|
1667
|
+
[node]
|
|
1668
|
+
);
|
|
1622
1669
|
}
|
|
1623
1670
|
};
|
|
1624
|
-
function lowerPythonBody(body, terminals) {
|
|
1671
|
+
function lowerPythonBody(body, terminals, thrown = new NodeSet()) {
|
|
1625
1672
|
const lowerer = new Lowerer(
|
|
1626
|
-
new Map(terminals.map((terminal) => [terminal.id, terminal]))
|
|
1673
|
+
new Map(terminals.map((terminal) => [terminal.id, terminal])),
|
|
1674
|
+
thrown
|
|
1627
1675
|
);
|
|
1628
1676
|
return {
|
|
1629
1677
|
statements: lowerer.lowerBlock(body),
|
|
@@ -1820,1171 +1868,1443 @@ function invocationEffects(definitionNode) {
|
|
|
1820
1868
|
});
|
|
1821
1869
|
}
|
|
1822
1870
|
|
|
1823
|
-
// src/paths/
|
|
1824
|
-
var
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1871
|
+
// src/paths/raisedResponses.ts
|
|
1872
|
+
var import_values5 = require("@suss/values");
|
|
1873
|
+
|
|
1874
|
+
// src/values/evaluator.ts
|
|
1875
|
+
var import_resolution2 = require("@suss/resolution");
|
|
1876
|
+
var import_values3 = require("@suss/values");
|
|
1877
|
+
|
|
1878
|
+
// src/values/lowering.ts
|
|
1879
|
+
var ROOT_TYPES = /* @__PURE__ */ new Set(["module", "function_definition", "lambda"]);
|
|
1880
|
+
var LITERAL_TYPES = {
|
|
1881
|
+
integer: (node) => ({ kind: "literal", value: Number(node.text) }),
|
|
1882
|
+
float: (node) => ({ kind: "literal", value: Number(node.text) }),
|
|
1883
|
+
true: () => ({ kind: "literal", value: true }),
|
|
1884
|
+
false: () => ({ kind: "literal", value: false }),
|
|
1885
|
+
none: () => ({ kind: "literal", value: null })
|
|
1829
1886
|
};
|
|
1830
|
-
|
|
1831
|
-
|
|
1887
|
+
var WRITING_ROWS = /* @__PURE__ */ new Set(["append", "extend"]);
|
|
1888
|
+
function readingMethodsOf(rows) {
|
|
1889
|
+
return new Set(
|
|
1890
|
+
rows.flatMap(
|
|
1891
|
+
(row) => row.kind === "method" && !WRITING_ROWS.has(row.method) ? [row.method] : []
|
|
1892
|
+
)
|
|
1893
|
+
);
|
|
1832
1894
|
}
|
|
1833
|
-
function
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1895
|
+
function pythonLowering(options) {
|
|
1896
|
+
const { context, originOf: originOf2, rows } = options;
|
|
1897
|
+
const readingMethods = readingMethodsOf(rows);
|
|
1898
|
+
const mutatedByRoot = /* @__PURE__ */ new Map();
|
|
1899
|
+
return {
|
|
1900
|
+
idOf: (node) => node.id,
|
|
1901
|
+
expression: (node) => expressionOf(node, originOf2),
|
|
1902
|
+
statement: statementOf,
|
|
1903
|
+
siteOf,
|
|
1904
|
+
functionOf,
|
|
1905
|
+
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
1906
|
+
callable: (node) => {
|
|
1907
|
+
if (context === null || hasDictionarySplat(node)) {
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
return context.callable(node);
|
|
1911
|
+
},
|
|
1912
|
+
mutatedInNestedFunction: (root, name) => {
|
|
1913
|
+
let mutated = mutatedByRoot.get(root.id);
|
|
1914
|
+
if (mutated === void 0) {
|
|
1915
|
+
mutated = namesMutatedInNestedFunctions(root, readingMethods);
|
|
1916
|
+
mutatedByRoot.set(root.id, mutated);
|
|
1917
|
+
}
|
|
1918
|
+
return mutated.has(name);
|
|
1919
|
+
},
|
|
1920
|
+
freeNamesOf,
|
|
1921
|
+
holeNameOf: placeholderName,
|
|
1922
|
+
rows
|
|
1923
|
+
};
|
|
1850
1924
|
}
|
|
1851
|
-
function
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
const scalar = SCALARS[node.type];
|
|
1856
|
-
if (scalar !== void 0) {
|
|
1857
|
-
return scalar;
|
|
1925
|
+
function placeholderName(node) {
|
|
1926
|
+
const peeled = peelValue(node);
|
|
1927
|
+
if (peeled.type === "identifier") {
|
|
1928
|
+
return peeled.text;
|
|
1858
1929
|
}
|
|
1859
|
-
if (
|
|
1860
|
-
return
|
|
1930
|
+
if (peeled.type === "attribute") {
|
|
1931
|
+
return field(peeled, "attribute")?.text ?? "param";
|
|
1861
1932
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1933
|
+
return "param";
|
|
1934
|
+
}
|
|
1935
|
+
function peelValue(node) {
|
|
1936
|
+
const wraps = node.type === "parenthesized_expression" || node.type === "await";
|
|
1937
|
+
const inner = wraps ? children(node)[0] : void 0;
|
|
1938
|
+
return inner === void 0 ? node : peelValue(inner);
|
|
1939
|
+
}
|
|
1940
|
+
var OPAQUE = { kind: "opaque" };
|
|
1941
|
+
function fromFields(node, names, fallback2, build) {
|
|
1942
|
+
const found = names.map((name) => field(node, name));
|
|
1943
|
+
if (found.some((part) => part === null)) {
|
|
1944
|
+
return fallback2;
|
|
1865
1945
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1946
|
+
return build(...found);
|
|
1947
|
+
}
|
|
1948
|
+
function expressionOf(node, originOf2) {
|
|
1949
|
+
const peeled = peelValue(node);
|
|
1950
|
+
const literal = LITERAL_TYPES[peeled.type];
|
|
1951
|
+
if (literal !== void 0) {
|
|
1952
|
+
return literal(peeled);
|
|
1868
1953
|
}
|
|
1869
|
-
if (
|
|
1870
|
-
return
|
|
1954
|
+
if (peeled.type === "string") {
|
|
1955
|
+
return stringExpression(peeled);
|
|
1871
1956
|
}
|
|
1872
|
-
if (
|
|
1873
|
-
const first = children2(node)[0];
|
|
1957
|
+
if (peeled.type === "concatenated_string") {
|
|
1874
1958
|
return {
|
|
1875
|
-
|
|
1876
|
-
|
|
1959
|
+
kind: "template",
|
|
1960
|
+
parts: children(peeled).map((part) => ({ expression: part }))
|
|
1877
1961
|
};
|
|
1878
1962
|
}
|
|
1879
|
-
if (
|
|
1880
|
-
return {
|
|
1881
|
-
}
|
|
1882
|
-
return { type: "unknown" };
|
|
1883
|
-
}
|
|
1884
|
-
function returnedBodyShape(statement) {
|
|
1885
|
-
const returned = statement.namedChildren[0];
|
|
1886
|
-
if (returned == null) {
|
|
1887
|
-
return null;
|
|
1888
|
-
}
|
|
1889
|
-
const value = returned.type === "expression_list" ? children2(returned)[0] : returned;
|
|
1890
|
-
if (value === void 0) {
|
|
1891
|
-
return null;
|
|
1963
|
+
if (peeled.type === "identifier") {
|
|
1964
|
+
return { kind: "name", text: peeled.text };
|
|
1892
1965
|
}
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1966
|
+
if (peeled.type === "attribute") {
|
|
1967
|
+
return fromFields(
|
|
1968
|
+
peeled,
|
|
1969
|
+
["object", "attribute"],
|
|
1970
|
+
OPAQUE,
|
|
1971
|
+
(object, name) => ({
|
|
1972
|
+
kind: "member",
|
|
1973
|
+
object,
|
|
1974
|
+
name: name.text
|
|
1975
|
+
})
|
|
1976
|
+
);
|
|
1903
1977
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
const
|
|
1907
|
-
if (
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1910
|
-
const statement = statementIn(call);
|
|
1911
|
-
if (statement === null) {
|
|
1912
|
-
continue;
|
|
1913
|
-
}
|
|
1914
|
-
for (const access of (0, import_sql.readSqlAccess)(statement, {
|
|
1915
|
-
dialect: pattern.storageSystem
|
|
1916
|
-
})) {
|
|
1917
|
-
effects.push({
|
|
1918
|
-
type: "interaction",
|
|
1919
|
-
binding: (0, import_ir_core.storageBinding)({
|
|
1920
|
-
recognition: `python-${pattern.module}`,
|
|
1921
|
-
storageSystem: pattern.storageSystem,
|
|
1922
|
-
scope: pattern.module,
|
|
1923
|
-
container: access.table
|
|
1924
|
-
}),
|
|
1925
|
-
callee: field(call, "function")?.text ?? "",
|
|
1926
|
-
interaction: {
|
|
1927
|
-
class: "storage-access",
|
|
1928
|
-
kind: access.kind,
|
|
1929
|
-
fields: access.fields,
|
|
1930
|
-
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
1931
|
-
operation: field(call, "function")?.text ?? ""
|
|
1932
|
-
}
|
|
1933
|
-
});
|
|
1978
|
+
if (peeled.type === "subscript") {
|
|
1979
|
+
const object = field(peeled, "value");
|
|
1980
|
+
const index = field(peeled, "subscript");
|
|
1981
|
+
if (object === null || index === null || index.type === "slice") {
|
|
1982
|
+
return { kind: "opaque" };
|
|
1934
1983
|
}
|
|
1984
|
+
return { kind: "element", object, index };
|
|
1935
1985
|
}
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
function patternFor(call, options) {
|
|
1939
|
-
const callee = field(call, "function");
|
|
1940
|
-
if (callee === null || callee.type !== "identifier") {
|
|
1941
|
-
return void 0;
|
|
1986
|
+
if (peeled.type === "list" || peeled.type === "tuple" || peeled.type === "expression_list") {
|
|
1987
|
+
return { kind: "array", items: children(peeled).map(elementOf) };
|
|
1942
1988
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
return void 0;
|
|
1989
|
+
if (peeled.type === "dictionary") {
|
|
1990
|
+
return { kind: "record", fields: children(peeled).flatMap(fieldOf) };
|
|
1946
1991
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
(pattern) => pattern.module === module2 && pattern.functions.includes(callee.text)
|
|
1950
|
-
);
|
|
1951
|
-
}
|
|
1952
|
-
function statementIn(call) {
|
|
1953
|
-
const args = field(call, "arguments");
|
|
1954
|
-
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
1955
|
-
if (first === null || first.type !== "string") {
|
|
1956
|
-
return null;
|
|
1992
|
+
if (peeled.type === "call") {
|
|
1993
|
+
return callExpression(peeled, originOf2);
|
|
1957
1994
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
const statement = (0, import_sql.sqlFromParts)(parts);
|
|
1972
|
-
return statement.trim() === "" ? null : statement;
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
// src/storage.ts
|
|
1976
|
-
var import_ir_core2 = require("@suss/ir-core");
|
|
1977
|
-
function receiverCall(call) {
|
|
1978
|
-
const callee = field(call, "function");
|
|
1979
|
-
if (callee === null || callee.type !== "attribute") {
|
|
1980
|
-
return null;
|
|
1995
|
+
if (peeled.type === "binary_operator" || peeled.type === "boolean_operator") {
|
|
1996
|
+
return fromFields(
|
|
1997
|
+
peeled,
|
|
1998
|
+
["left", "operator", "right"],
|
|
1999
|
+
OPAQUE,
|
|
2000
|
+
(left, operator, right) => ({
|
|
2001
|
+
kind: "operator",
|
|
2002
|
+
operator: operator.text,
|
|
2003
|
+
operands: [left, right]
|
|
2004
|
+
})
|
|
2005
|
+
);
|
|
1981
2006
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
}
|
|
1985
|
-
function methodName(call) {
|
|
1986
|
-
const callee = field(call, "function");
|
|
1987
|
-
if (callee === null || callee.type !== "attribute") {
|
|
1988
|
-
return callee?.text ?? "";
|
|
2007
|
+
if (peeled.type === "comparison_operator") {
|
|
2008
|
+
return comparisonExpression(peeled);
|
|
1989
2009
|
}
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2010
|
+
if (peeled.type === "not_operator") {
|
|
2011
|
+
return fromFields(
|
|
2012
|
+
peeled,
|
|
2013
|
+
["argument"],
|
|
2014
|
+
OPAQUE,
|
|
2015
|
+
(argument) => ({
|
|
2016
|
+
kind: "operator",
|
|
2017
|
+
operator: "not",
|
|
2018
|
+
operands: [argument]
|
|
2019
|
+
})
|
|
2020
|
+
);
|
|
1996
2021
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
return receiver === null ? call : rootOf(receiver);
|
|
2002
|
-
}
|
|
2003
|
-
function chainsIn(calls) {
|
|
2004
|
-
const endsWith = /* @__PURE__ */ new Map();
|
|
2005
|
-
for (const call of calls) {
|
|
2006
|
-
const receiver = receiverCall(call);
|
|
2007
|
-
if (receiver === null) {
|
|
2008
|
-
continue;
|
|
2009
|
-
}
|
|
2010
|
-
const start = rootOf(receiver);
|
|
2011
|
-
const kept = endsWith.get(start.id);
|
|
2012
|
-
if (kept === void 0 || call.endIndex > kept.endIndex) {
|
|
2013
|
-
endsWith.set(start.id, call);
|
|
2022
|
+
if (peeled.type === "conditional_expression") {
|
|
2023
|
+
const [whenTrue, condition, whenFalse] = children(peeled);
|
|
2024
|
+
if (whenTrue === void 0 || condition === void 0 || whenFalse === void 0) {
|
|
2025
|
+
return { kind: "opaque" };
|
|
2014
2026
|
}
|
|
2027
|
+
return { kind: "conditional", condition, whenTrue, whenFalse };
|
|
2015
2028
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
if (receiverCall(call) !== null) {
|
|
2019
|
-
continue;
|
|
2020
|
-
}
|
|
2021
|
-
const last = endsWith.get(call.id) ?? call;
|
|
2022
|
-
chains.push({
|
|
2023
|
-
root: call,
|
|
2024
|
-
last,
|
|
2025
|
-
subject: subjectOf(call),
|
|
2026
|
-
operation: methodName(last),
|
|
2027
|
-
between: calls.filter(
|
|
2028
|
-
(other) => other !== call && other !== last && other.startIndex >= call.startIndex && other.endIndex <= last.endIndex
|
|
2029
|
-
)
|
|
2030
|
-
});
|
|
2029
|
+
if (peeled.type === "lambda") {
|
|
2030
|
+
return { kind: "function", node: peeled };
|
|
2031
2031
|
}
|
|
2032
|
-
return
|
|
2032
|
+
return { kind: "opaque" };
|
|
2033
2033
|
}
|
|
2034
|
-
function
|
|
2035
|
-
const
|
|
2036
|
-
if (
|
|
2037
|
-
return
|
|
2038
|
-
}
|
|
2039
|
-
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
2040
|
-
if (from === void 0) {
|
|
2041
|
-
return void 0;
|
|
2034
|
+
function stringExpression(node) {
|
|
2035
|
+
const plain = stringLiteralValue(node);
|
|
2036
|
+
if (plain !== null) {
|
|
2037
|
+
return { kind: "literal", value: plain };
|
|
2042
2038
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
}
|
|
2047
|
-
function fieldsOf(chain) {
|
|
2048
|
-
const args = field(chain.root, "arguments");
|
|
2049
|
-
const named = [];
|
|
2050
|
-
for (const argument of args === null ? [] : children(args)) {
|
|
2051
|
-
if (argument.type === "keyword_argument") {
|
|
2052
|
-
const name = field(argument, "name");
|
|
2053
|
-
if (name !== null) {
|
|
2054
|
-
named.push(name.text);
|
|
2055
|
-
}
|
|
2039
|
+
const parts = [];
|
|
2040
|
+
for (const child of children(node)) {
|
|
2041
|
+
if (child.type === "string_content") {
|
|
2042
|
+
parts.push({ text: stringContentValue(child) });
|
|
2056
2043
|
continue;
|
|
2057
2044
|
}
|
|
2058
|
-
if (
|
|
2059
|
-
const
|
|
2060
|
-
if (
|
|
2061
|
-
|
|
2045
|
+
if (child.type === "interpolation") {
|
|
2046
|
+
const expression = field(child, "expression") ?? children(child)[0];
|
|
2047
|
+
if (expression !== void 0) {
|
|
2048
|
+
parts.push({ expression });
|
|
2062
2049
|
}
|
|
2063
2050
|
}
|
|
2064
2051
|
}
|
|
2065
|
-
return
|
|
2052
|
+
return { kind: "template", parts };
|
|
2066
2053
|
}
|
|
2067
|
-
function
|
|
2068
|
-
const
|
|
2069
|
-
const
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2054
|
+
function comparisonExpression(node) {
|
|
2055
|
+
const operands = children(node);
|
|
2056
|
+
const operators = node.children.filter(
|
|
2057
|
+
(child) => child !== null && !child.isNamed
|
|
2058
|
+
);
|
|
2059
|
+
const operator = operators[0];
|
|
2060
|
+
if (operands.length !== 2 || operators.length !== 1 || operator === void 0) {
|
|
2061
|
+
return { kind: "opaque" };
|
|
2077
2062
|
}
|
|
2078
|
-
return picked;
|
|
2079
|
-
}
|
|
2080
|
-
function effectFor(pattern, chain, options) {
|
|
2081
|
-
const picked = [
|
|
2082
|
-
...new Set([chain.last, ...chain.between].flatMap(selectorOf))
|
|
2083
|
-
];
|
|
2084
2063
|
return {
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
storageSystem: pattern.storageSystem,
|
|
2089
|
-
scope: pattern.module,
|
|
2090
|
-
container: chain.subject
|
|
2091
|
-
}),
|
|
2092
|
-
callee: chain.last.text,
|
|
2093
|
-
interaction: {
|
|
2094
|
-
class: "storage-access",
|
|
2095
|
-
kind: pattern.writes.includes(chain.operation) ? "write" : "read",
|
|
2096
|
-
fields: fieldsOf(chain),
|
|
2097
|
-
operation: chain.operation,
|
|
2098
|
-
...picked.length > 0 ? { selector: picked } : {}
|
|
2099
|
-
}
|
|
2064
|
+
kind: "operator",
|
|
2065
|
+
operator: operator.text,
|
|
2066
|
+
operands: [operands[0], operands[1]]
|
|
2100
2067
|
};
|
|
2101
2068
|
}
|
|
2102
|
-
function
|
|
2103
|
-
|
|
2104
|
-
|
|
2069
|
+
function callExpression(node, originOf2) {
|
|
2070
|
+
return fromFields(
|
|
2071
|
+
node,
|
|
2072
|
+
["function", "arguments"],
|
|
2073
|
+
OPAQUE,
|
|
2074
|
+
(callee, argumentList) => {
|
|
2075
|
+
const peeledCallee = peelValue(callee);
|
|
2076
|
+
const args = argumentList.type === "argument_list" ? argumentsOf(argumentList) : [];
|
|
2077
|
+
const isMethod2 = peeledCallee.type === "attribute";
|
|
2078
|
+
return {
|
|
2079
|
+
kind: "call",
|
|
2080
|
+
callee: {
|
|
2081
|
+
receiver: isMethod2 ? field(peeledCallee, "object") : null,
|
|
2082
|
+
name: calleeName(peeledCallee),
|
|
2083
|
+
origin: () => originOf2(peeledCallee)
|
|
2084
|
+
},
|
|
2085
|
+
args,
|
|
2086
|
+
constructs: false
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
);
|
|
2105
2090
|
}
|
|
2106
|
-
function
|
|
2107
|
-
if (
|
|
2108
|
-
return
|
|
2091
|
+
function calleeName(callee) {
|
|
2092
|
+
if (callee.type === "identifier") {
|
|
2093
|
+
return callee.text;
|
|
2109
2094
|
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
return null;
|
|
2095
|
+
if (callee.type === "attribute") {
|
|
2096
|
+
return field(callee, "attribute")?.text ?? null;
|
|
2113
2097
|
}
|
|
2114
|
-
return
|
|
2098
|
+
return null;
|
|
2115
2099
|
}
|
|
2116
|
-
function
|
|
2117
|
-
return
|
|
2100
|
+
function argumentsOf(argumentList) {
|
|
2101
|
+
return children(argumentList).flatMap((argument) => {
|
|
2102
|
+
if (argument.type === "dictionary_splat") {
|
|
2103
|
+
return [];
|
|
2104
|
+
}
|
|
2105
|
+
if (argument.type === "keyword_argument") {
|
|
2106
|
+
const name = field(argument, "name");
|
|
2107
|
+
const value = field(argument, "value");
|
|
2108
|
+
return name === null || value === null ? [] : [{ kind: "named", name: name.text, node: value }];
|
|
2109
|
+
}
|
|
2110
|
+
return [elementOf(argument)];
|
|
2111
|
+
});
|
|
2118
2112
|
}
|
|
2119
|
-
function
|
|
2120
|
-
|
|
2121
|
-
|
|
2113
|
+
function hasDictionarySplat(call) {
|
|
2114
|
+
const argumentList = field(call, "arguments");
|
|
2115
|
+
if (argumentList === null) {
|
|
2116
|
+
return false;
|
|
2122
2117
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
);
|
|
2126
|
-
const chains = chainsIn(calls).filter(
|
|
2127
|
-
(chain) => options.couldMatch.has(methodName(chain.root)) || named.has(methodName(chain.root))
|
|
2118
|
+
return children(argumentList).some(
|
|
2119
|
+
(argument) => argument.type === "dictionary_splat"
|
|
2128
2120
|
);
|
|
2129
|
-
|
|
2121
|
+
}
|
|
2122
|
+
function elementOf(node) {
|
|
2123
|
+
if (node.type === "list_splat") {
|
|
2124
|
+
const inner = children(node)[0];
|
|
2125
|
+
return inner === void 0 ? { kind: "value", node } : { kind: "spread", node: inner };
|
|
2126
|
+
}
|
|
2127
|
+
return { kind: "value", node };
|
|
2128
|
+
}
|
|
2129
|
+
function fieldOf(node) {
|
|
2130
|
+
if (node.type === "dictionary_splat") {
|
|
2131
|
+
const inner = children(node)[0];
|
|
2132
|
+
return inner === void 0 ? [] : [{ kind: "spread", node: inner }];
|
|
2133
|
+
}
|
|
2134
|
+
if (node.type !== "pair") {
|
|
2130
2135
|
return [];
|
|
2131
2136
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2154
|
-
if (settled === void 0) {
|
|
2155
|
-
continue;
|
|
2156
|
-
}
|
|
2157
|
-
const typeName = returnTypeName(options.definitionAt(settled));
|
|
2158
|
-
if (typeName === null) {
|
|
2159
|
-
continue;
|
|
2160
|
-
}
|
|
2161
|
-
const pattern = options.patterns.find(
|
|
2162
|
-
(candidate) => candidate.queryTypes.includes(typeName) && options.facts.facts("pyImport").some(
|
|
2163
|
-
(row) => String(row[0]) === fileOf(settled) && String(row[1]) === candidate.module
|
|
2137
|
+
return fromFields(node, ["key", "value"], [], (key, value) => {
|
|
2138
|
+
const name = stringLiteralValue(key);
|
|
2139
|
+
return name === null ? [{ kind: "computed", name: key, value }] : [{ kind: "field", name, value }];
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
var STATEMENT_TYPES = {
|
|
2143
|
+
expression_statement: expressionStatement,
|
|
2144
|
+
if_statement: ifStatement,
|
|
2145
|
+
elif_clause: ifStatement,
|
|
2146
|
+
for_statement: loopStatement,
|
|
2147
|
+
while_statement: loopStatement,
|
|
2148
|
+
return_statement: (node) => ({
|
|
2149
|
+
kind: "return",
|
|
2150
|
+
value: children(node)[0] ?? null
|
|
2151
|
+
}),
|
|
2152
|
+
try_statement: (node) => ({
|
|
2153
|
+
kind: "block",
|
|
2154
|
+
body: [
|
|
2155
|
+
...blockStatements2(field(node, "body")),
|
|
2156
|
+
...children(node).flatMap(
|
|
2157
|
+
(clause) => clause.type === "finally_clause" ? blockStatements2(children(clause)[0] ?? null) : []
|
|
2164
2158
|
)
|
|
2159
|
+
]
|
|
2160
|
+
}),
|
|
2161
|
+
with_statement: (node) => ({
|
|
2162
|
+
kind: "block",
|
|
2163
|
+
body: blockStatements2(field(node, "body"))
|
|
2164
|
+
})
|
|
2165
|
+
};
|
|
2166
|
+
function statementOf(node) {
|
|
2167
|
+
const lower = STATEMENT_TYPES[node.type];
|
|
2168
|
+
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2169
|
+
}
|
|
2170
|
+
function expressionStatement(node) {
|
|
2171
|
+
const inner = children(node)[0];
|
|
2172
|
+
if (inner === void 0) {
|
|
2173
|
+
return { kind: "opaque" };
|
|
2174
|
+
}
|
|
2175
|
+
if (inner.type === "assignment") {
|
|
2176
|
+
return assignment(inner, null);
|
|
2177
|
+
}
|
|
2178
|
+
if (inner.type === "augmented_assignment") {
|
|
2179
|
+
const operator = field(inner, "operator");
|
|
2180
|
+
return assignment(
|
|
2181
|
+
inner,
|
|
2182
|
+
operator === null ? null : operator.text.slice(0, -1)
|
|
2165
2183
|
);
|
|
2166
|
-
if (pattern === void 0) {
|
|
2167
|
-
continue;
|
|
2168
|
-
}
|
|
2169
|
-
effects.push(effectFor(pattern, chain, options));
|
|
2170
2184
|
}
|
|
2171
|
-
return
|
|
2185
|
+
return { kind: "expression", value: inner };
|
|
2172
2186
|
}
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
}
|
|
2187
|
-
var WRITING_ROWS = /* @__PURE__ */ new Set(["append", "extend"]);
|
|
2188
|
-
function readingMethodsOf(rows) {
|
|
2189
|
-
return new Set(
|
|
2190
|
-
rows.flatMap(
|
|
2191
|
-
(row) => row.kind === "method" && !WRITING_ROWS.has(row.method) ? [row.method] : []
|
|
2192
|
-
)
|
|
2187
|
+
function assignment(node, operator) {
|
|
2188
|
+
return fromFields(
|
|
2189
|
+
node,
|
|
2190
|
+
["left", "right"],
|
|
2191
|
+
OPAQUE,
|
|
2192
|
+
(target, value) => {
|
|
2193
|
+
if (target.type === "pattern_list" || target.type === "tuple_pattern") {
|
|
2194
|
+
return {
|
|
2195
|
+
kind: "declare",
|
|
2196
|
+
bindings: patternNames(target).map((name) => ({ name, value: null }))
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
return { kind: "assign", target, operator, value };
|
|
2200
|
+
}
|
|
2193
2201
|
);
|
|
2194
2202
|
}
|
|
2195
|
-
function
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
return {
|
|
2200
|
-
idOf: (node) => node.id,
|
|
2201
|
-
expression: (node) => expressionOf(node, originOf2),
|
|
2202
|
-
statement: statementOf,
|
|
2203
|
-
siteOf,
|
|
2204
|
-
functionOf,
|
|
2205
|
-
writtenTo: (node) => context === null ? null : context.writtenTo(node),
|
|
2206
|
-
callable: (node) => {
|
|
2207
|
-
if (context === null || hasDictionarySplat(node)) {
|
|
2208
|
-
return null;
|
|
2209
|
-
}
|
|
2210
|
-
return context.callable(node);
|
|
2211
|
-
},
|
|
2212
|
-
mutatedInNestedFunction: (root, name) => {
|
|
2213
|
-
let mutated = mutatedByRoot.get(root.id);
|
|
2214
|
-
if (mutated === void 0) {
|
|
2215
|
-
mutated = namesMutatedInNestedFunctions(root, readingMethods);
|
|
2216
|
-
mutatedByRoot.set(root.id, mutated);
|
|
2217
|
-
}
|
|
2218
|
-
return mutated.has(name);
|
|
2219
|
-
},
|
|
2220
|
-
freeNamesOf,
|
|
2221
|
-
holeNameOf: placeholderName,
|
|
2222
|
-
rows
|
|
2223
|
-
};
|
|
2203
|
+
function patternNames(pattern) {
|
|
2204
|
+
return children(pattern).flatMap(
|
|
2205
|
+
(child) => child.type === "identifier" ? [child.text] : patternNames(child)
|
|
2206
|
+
);
|
|
2224
2207
|
}
|
|
2225
|
-
function
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2208
|
+
function ifStatement(node) {
|
|
2209
|
+
return fromFields(
|
|
2210
|
+
node,
|
|
2211
|
+
["condition", "consequence"],
|
|
2212
|
+
OPAQUE,
|
|
2213
|
+
(condition, consequence) => ({
|
|
2214
|
+
kind: "branch",
|
|
2215
|
+
condition,
|
|
2216
|
+
arms: [blockStatements2(consequence), otherArm(node)]
|
|
2217
|
+
})
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
function otherArm(clause) {
|
|
2221
|
+
const statement = clause.type === "if_statement" ? clause : clause.parent;
|
|
2222
|
+
const clauses = statement === null ? [] : children(statement);
|
|
2223
|
+
const next = clauses.slice(clauses.findIndex((child) => child.id === clause.id) + 1).find(
|
|
2224
|
+
(child) => child.type === "elif_clause" || child.type === "else_clause"
|
|
2225
|
+
);
|
|
2226
|
+
if (next === void 0) {
|
|
2227
|
+
return [];
|
|
2232
2228
|
}
|
|
2233
|
-
return "
|
|
2229
|
+
return next.type === "elif_clause" ? [next] : blockStatements2(field(next, "body"));
|
|
2234
2230
|
}
|
|
2235
|
-
function
|
|
2236
|
-
|
|
2237
|
-
const inner = wraps ? children(node)[0] : void 0;
|
|
2238
|
-
return inner === void 0 ? node : peelValue(inner);
|
|
2231
|
+
function loopStatement(node) {
|
|
2232
|
+
return { kind: "loop", body: blockStatements2(field(node, "body")) };
|
|
2239
2233
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
const found = names.map((name) => field(node, name));
|
|
2243
|
-
if (found.some((part) => part === null)) {
|
|
2244
|
-
return fallback2;
|
|
2245
|
-
}
|
|
2246
|
-
return build(...found);
|
|
2234
|
+
function blockStatements2(block) {
|
|
2235
|
+
return block === null ? [] : children(block);
|
|
2247
2236
|
}
|
|
2248
|
-
function
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
};
|
|
2262
|
-
}
|
|
2263
|
-
if (peeled.type === "identifier") {
|
|
2264
|
-
return { kind: "name", text: peeled.text };
|
|
2265
|
-
}
|
|
2266
|
-
if (peeled.type === "attribute") {
|
|
2267
|
-
return fromFields(
|
|
2268
|
-
peeled,
|
|
2269
|
-
["object", "attribute"],
|
|
2270
|
-
OPAQUE,
|
|
2271
|
-
(object, name) => ({
|
|
2272
|
-
kind: "member",
|
|
2273
|
-
object,
|
|
2274
|
-
name: name.text
|
|
2275
|
-
})
|
|
2276
|
-
);
|
|
2277
|
-
}
|
|
2278
|
-
if (peeled.type === "subscript") {
|
|
2279
|
-
const object = field(peeled, "value");
|
|
2280
|
-
const index = field(peeled, "subscript");
|
|
2281
|
-
if (object === null || index === null || index.type === "slice") {
|
|
2282
|
-
return { kind: "opaque" };
|
|
2237
|
+
function isRoot(node) {
|
|
2238
|
+
return ROOT_TYPES.has(node.type);
|
|
2239
|
+
}
|
|
2240
|
+
function isPathStatement(node, parent) {
|
|
2241
|
+
return parent.type === "module" || parent.type === "block" || node.type === "elif_clause";
|
|
2242
|
+
}
|
|
2243
|
+
function siteOf(node) {
|
|
2244
|
+
const path6 = [];
|
|
2245
|
+
let current = node;
|
|
2246
|
+
for (; ; ) {
|
|
2247
|
+
const parent = current.parent;
|
|
2248
|
+
if (parent === null) {
|
|
2249
|
+
return null;
|
|
2283
2250
|
}
|
|
2284
|
-
|
|
2251
|
+
if (isPathStatement(current, parent)) {
|
|
2252
|
+
path6.unshift(current);
|
|
2253
|
+
}
|
|
2254
|
+
if (isRoot(parent)) {
|
|
2255
|
+
return { root: parent, path: path6 };
|
|
2256
|
+
}
|
|
2257
|
+
current = parent;
|
|
2285
2258
|
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2259
|
+
}
|
|
2260
|
+
function functionOf(node) {
|
|
2261
|
+
if (node.type === "module") {
|
|
2262
|
+
return { parameters: [], body: children(node) };
|
|
2288
2263
|
}
|
|
2289
|
-
if (
|
|
2290
|
-
|
|
2264
|
+
if (node.type === "lambda") {
|
|
2265
|
+
const body = field(node, "body");
|
|
2266
|
+
if (body === null) {
|
|
2267
|
+
return null;
|
|
2268
|
+
}
|
|
2269
|
+
return { parameters: parameterShapes(node), body: { expression: body } };
|
|
2291
2270
|
}
|
|
2292
|
-
if (
|
|
2293
|
-
return
|
|
2271
|
+
if (node.type !== "function_definition") {
|
|
2272
|
+
return null;
|
|
2294
2273
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2274
|
+
const parameters = parameterShapes(node);
|
|
2275
|
+
return {
|
|
2276
|
+
parameters: isMethod(node) ? parameters.slice(1) : parameters,
|
|
2277
|
+
body: blockStatements2(field(node, "body"))
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function isMethod(fn) {
|
|
2281
|
+
const owner = fn.parent?.type === "decorated_definition" ? fn.parent.parent : fn.parent;
|
|
2282
|
+
return owner?.type === "block" && owner.parent?.type === "class_definition";
|
|
2283
|
+
}
|
|
2284
|
+
function namesMutatedInNestedFunctions(root, readingMethods) {
|
|
2285
|
+
const mutated = /* @__PURE__ */ new Set();
|
|
2286
|
+
const visit = (node, nested) => {
|
|
2287
|
+
const name = writtenNameOf(node, readingMethods);
|
|
2288
|
+
if (nested && name !== null) {
|
|
2289
|
+
mutated.add(name);
|
|
2290
|
+
}
|
|
2291
|
+
for (const child of children(node)) {
|
|
2292
|
+
visit(child, nested || isRoot(child));
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
for (const child of children(root)) {
|
|
2296
|
+
visit(child, isRoot(child));
|
|
2297
|
+
}
|
|
2298
|
+
return mutated;
|
|
2299
|
+
}
|
|
2300
|
+
function writtenNameOf(node, readingMethods) {
|
|
2301
|
+
if (node.type === "assignment" || node.type === "augmented_assignment") {
|
|
2302
|
+
const target = field(node, "left");
|
|
2303
|
+
return target?.type === "identifier" ? target.text : null;
|
|
2304
|
+
}
|
|
2305
|
+
if (node.type === "call") {
|
|
2306
|
+
const callee = field(node, "function");
|
|
2307
|
+
if (callee?.type !== "attribute") {
|
|
2308
|
+
return null;
|
|
2309
|
+
}
|
|
2310
|
+
const receiver = field(callee, "object");
|
|
2311
|
+
const method = field(callee, "attribute");
|
|
2312
|
+
if (receiver?.type === "identifier" && method !== null && !readingMethods.has(method.text)) {
|
|
2313
|
+
return receiver.text;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
return null;
|
|
2317
|
+
}
|
|
2318
|
+
var DECLARING_PARENTS = /* @__PURE__ */ new Set([
|
|
2319
|
+
"parameters",
|
|
2320
|
+
"default_parameter",
|
|
2321
|
+
"typed_parameter",
|
|
2322
|
+
"typed_default_parameter",
|
|
2323
|
+
"lambda_parameters",
|
|
2324
|
+
"pattern_list",
|
|
2325
|
+
"tuple_pattern",
|
|
2326
|
+
"for_statement"
|
|
2327
|
+
]);
|
|
2328
|
+
function freeNamesOf(fn) {
|
|
2329
|
+
const declared = /* @__PURE__ */ new Set();
|
|
2330
|
+
const read = /* @__PURE__ */ new Set();
|
|
2331
|
+
const visit = (node) => {
|
|
2332
|
+
for (const child of children(node)) {
|
|
2333
|
+
if (child.type !== "identifier") {
|
|
2334
|
+
visit(child);
|
|
2335
|
+
continue;
|
|
2336
|
+
}
|
|
2337
|
+
if (node.type === "attribute" && field(node, "attribute")?.id === child.id) {
|
|
2338
|
+
continue;
|
|
2339
|
+
}
|
|
2340
|
+
if (node.type === "keyword_argument" && field(node, "name")?.id === child.id) {
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
if (DECLARING_PARENTS.has(node.type) || node.type === "assignment" && field(node, "left")?.id === child.id) {
|
|
2344
|
+
declared.add(child.text);
|
|
2345
|
+
continue;
|
|
2346
|
+
}
|
|
2347
|
+
read.add(child.text);
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
visit(fn);
|
|
2351
|
+
return [...read].filter((name) => !declared.has(name));
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
// src/values/origin.ts
|
|
2355
|
+
function originOf(callee, module2) {
|
|
2356
|
+
const chain = memberChainOf(callee);
|
|
2357
|
+
if (chain === null) {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
const [rootName, ...rest] = chain;
|
|
2361
|
+
if (rootName === void 0) {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
const binding = resolveName(scopeAt(callee, module2), rootName);
|
|
2365
|
+
if (binding === null) {
|
|
2366
|
+
return rest.length === 0 ? { module: "builtins", name: rootName } : null;
|
|
2367
|
+
}
|
|
2368
|
+
const path6 = importedPathOf(binding, rootName);
|
|
2369
|
+
if (path6 === null) {
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
const dotted = [...path6, ...rest];
|
|
2373
|
+
const name = dotted.pop();
|
|
2374
|
+
if (name === void 0 || dotted.length === 0) {
|
|
2375
|
+
return null;
|
|
2376
|
+
}
|
|
2377
|
+
return { module: dotted.join("."), name };
|
|
2378
|
+
}
|
|
2379
|
+
function importedPathOf(binding, localName) {
|
|
2380
|
+
if (binding.kind === "importFrom") {
|
|
2381
|
+
if (binding.relativeLevel > 0) {
|
|
2382
|
+
return null;
|
|
2383
|
+
}
|
|
2384
|
+
return [...binding.module.split("."), binding.importedName];
|
|
2385
|
+
}
|
|
2386
|
+
if (binding.kind !== "import") {
|
|
2387
|
+
return null;
|
|
2388
|
+
}
|
|
2389
|
+
const unaliasedDotted = binding.module !== localName && binding.module.startsWith(`${localName}.`);
|
|
2390
|
+
return unaliasedDotted ? [localName] : binding.module.split(".");
|
|
2391
|
+
}
|
|
2392
|
+
function memberChainOf(node) {
|
|
2393
|
+
const names = [];
|
|
2394
|
+
let current = peelValue(node);
|
|
2395
|
+
while (current.type === "attribute") {
|
|
2396
|
+
const object = field(current, "object");
|
|
2397
|
+
const attribute = field(current, "attribute");
|
|
2398
|
+
if (object === null || attribute === null) {
|
|
2399
|
+
return null;
|
|
2400
|
+
}
|
|
2401
|
+
names.unshift(attribute.text);
|
|
2402
|
+
current = peelValue(object);
|
|
2403
|
+
}
|
|
2404
|
+
if (current.type !== "identifier") {
|
|
2405
|
+
return null;
|
|
2406
|
+
}
|
|
2407
|
+
return [current.text, ...names];
|
|
2408
|
+
}
|
|
2409
|
+
function scopeAt(node, module2) {
|
|
2410
|
+
let current = node;
|
|
2411
|
+
while (current !== null) {
|
|
2412
|
+
const scope = module2.scopeFor.get(current.id);
|
|
2413
|
+
if (scope !== void 0) {
|
|
2414
|
+
return scope;
|
|
2415
|
+
}
|
|
2416
|
+
current = current.parent;
|
|
2417
|
+
}
|
|
2418
|
+
return module2.moduleScope;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
// src/values/rows.ts
|
|
2422
|
+
var import_values2 = require("@suss/values");
|
|
2423
|
+
var operatorRows = [
|
|
2424
|
+
{
|
|
2425
|
+
kind: "operator",
|
|
2426
|
+
operator: "+",
|
|
2427
|
+
arity: 2,
|
|
2428
|
+
apply: ([a, b]) => (0, import_values2.plus)((0, import_values2.operand)(a), (0, import_values2.operand)(b))
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
kind: "operator",
|
|
2432
|
+
operator: "%",
|
|
2433
|
+
arity: 2,
|
|
2434
|
+
apply: ([a, b], contentOf) => (0, import_values2.percentFormatted)((0, import_values2.operand)(a), (0, import_values2.formatArguments)(contentOf((0, import_values2.operand)(b))))
|
|
2435
|
+
},
|
|
2436
|
+
{
|
|
2437
|
+
kind: "operator",
|
|
2438
|
+
operator: "==",
|
|
2439
|
+
arity: 2,
|
|
2440
|
+
apply: ([a, b]) => (0, import_values2.equals)((0, import_values2.operand)(a), (0, import_values2.operand)(b))
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
kind: "operator",
|
|
2444
|
+
operator: "!=",
|
|
2445
|
+
arity: 2,
|
|
2446
|
+
apply: ([a, b]) => (0, import_values2.negated)((0, import_values2.equals)((0, import_values2.operand)(a), (0, import_values2.operand)(b)))
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
kind: "operator",
|
|
2450
|
+
operator: "is",
|
|
2451
|
+
arity: 2,
|
|
2452
|
+
apply: ([a, b]) => (0, import_values2.equals)((0, import_values2.operand)(a), (0, import_values2.operand)(b))
|
|
2453
|
+
},
|
|
2454
|
+
{
|
|
2455
|
+
kind: "operator",
|
|
2456
|
+
operator: "is not",
|
|
2457
|
+
arity: 2,
|
|
2458
|
+
apply: ([a, b]) => (0, import_values2.negated)((0, import_values2.equals)((0, import_values2.operand)(a), (0, import_values2.operand)(b)))
|
|
2459
|
+
},
|
|
2460
|
+
{
|
|
2461
|
+
kind: "operator",
|
|
2462
|
+
operator: "not",
|
|
2463
|
+
arity: 1,
|
|
2464
|
+
apply: ([a]) => (0, import_values2.negated)((0, import_values2.operand)(a))
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
kind: "operator",
|
|
2468
|
+
operator: "or",
|
|
2469
|
+
arity: 2,
|
|
2470
|
+
apply: ([a, b]) => (0, import_values2.readableFallback)((0, import_values2.operand)(a), (0, import_values2.operand)(b), import_values2.truthOf)
|
|
2471
|
+
},
|
|
2472
|
+
{
|
|
2473
|
+
kind: "operator",
|
|
2474
|
+
operator: "and",
|
|
2475
|
+
arity: 2,
|
|
2476
|
+
apply: ([a, b]) => (0, import_values2.fallback)((0, import_values2.operand)(a), (0, import_values2.operand)(b), (left) => {
|
|
2477
|
+
const truth = (0, import_values2.truthOf)(left);
|
|
2478
|
+
return truth === null ? null : !truth;
|
|
2479
|
+
})
|
|
2480
|
+
}
|
|
2481
|
+
];
|
|
2482
|
+
var stringRows = [
|
|
2483
|
+
{
|
|
2484
|
+
kind: "method",
|
|
2485
|
+
method: "join",
|
|
2486
|
+
on: "string",
|
|
2487
|
+
apply: ({ receiver, args, contentOf }) => ({
|
|
2488
|
+
result: (0, import_values2.joined)(contentOf((0, import_values2.operand)(args[0])), (0, import_values2.operand)(receiver))
|
|
2489
|
+
})
|
|
2490
|
+
},
|
|
2491
|
+
{
|
|
2492
|
+
kind: "method",
|
|
2493
|
+
method: "format",
|
|
2494
|
+
on: "string",
|
|
2495
|
+
apply: ({ receiver, args }) => ({
|
|
2496
|
+
result: braceFormatted((0, import_values2.operand)(receiver), args)
|
|
2497
|
+
})
|
|
2498
|
+
},
|
|
2499
|
+
{
|
|
2500
|
+
kind: "method",
|
|
2501
|
+
method: "strip",
|
|
2502
|
+
on: "string",
|
|
2503
|
+
apply: ({ receiver, args }) => ({
|
|
2504
|
+
result: args.length === 0 ? (0, import_values2.stripped)((0, import_values2.operand)(receiver)) : (0, import_values2.hole)("value")
|
|
2505
|
+
})
|
|
2506
|
+
},
|
|
2507
|
+
{
|
|
2508
|
+
kind: "method",
|
|
2509
|
+
method: "rstrip",
|
|
2510
|
+
on: "string",
|
|
2511
|
+
apply: ({ receiver, args }) => ({
|
|
2512
|
+
result: args.length === 0 ? (0, import_values2.stripped)((0, import_values2.operand)(receiver), "end") : (0, import_values2.hole)("value")
|
|
2513
|
+
})
|
|
2514
|
+
},
|
|
2515
|
+
{
|
|
2516
|
+
kind: "method",
|
|
2517
|
+
method: "lstrip",
|
|
2518
|
+
on: "string",
|
|
2519
|
+
apply: ({ receiver, args }) => ({
|
|
2520
|
+
result: args.length === 0 ? (0, import_values2.stripped)((0, import_values2.operand)(receiver), "start") : (0, import_values2.hole)("value")
|
|
2521
|
+
})
|
|
2522
|
+
}
|
|
2523
|
+
];
|
|
2524
|
+
var sequenceRows = ["sequence", "unbounded"].flatMap((on) => [
|
|
2525
|
+
{
|
|
2526
|
+
kind: "method",
|
|
2527
|
+
method: "append",
|
|
2528
|
+
on,
|
|
2529
|
+
apply: ({ receiver, args }) => ({
|
|
2530
|
+
result: (0, import_values2.constant)(null),
|
|
2531
|
+
receiver: (0, import_values2.appended)((0, import_values2.operand)(receiver), args)
|
|
2532
|
+
})
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
kind: "method",
|
|
2536
|
+
method: "extend",
|
|
2537
|
+
on,
|
|
2538
|
+
apply: ({ receiver, args, contentOf }) => ({
|
|
2539
|
+
result: (0, import_values2.constant)(null),
|
|
2540
|
+
receiver: (0, import_values2.extended)((0, import_values2.operand)(receiver), contentOf((0, import_values2.operand)(args[0])))
|
|
2541
|
+
})
|
|
2542
|
+
}
|
|
2543
|
+
]);
|
|
2544
|
+
var calleeRows = [
|
|
2545
|
+
{
|
|
2546
|
+
kind: "callee",
|
|
2547
|
+
origin: { module: "builtins", name: "str" },
|
|
2548
|
+
apply: ({ args }) => ({ result: (0, import_values2.concat)([(0, import_values2.operand)(args[0])]) })
|
|
2549
|
+
},
|
|
2550
|
+
{
|
|
2551
|
+
kind: "callee",
|
|
2552
|
+
origin: { module: "os.path", name: "join" },
|
|
2553
|
+
apply: ({ args }) => ({ result: (0, import_values2.joinedPath)(args) })
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
kind: "callee",
|
|
2557
|
+
origin: { module: "os", name: "getenv" },
|
|
2558
|
+
apply: ({ args }) => ({ result: (0, import_values2.environmentRead)(args) })
|
|
2559
|
+
},
|
|
2560
|
+
{
|
|
2561
|
+
kind: "callee",
|
|
2562
|
+
origin: { module: "os.environ", name: "get" },
|
|
2563
|
+
apply: ({ args }) => ({ result: (0, import_values2.environmentRead)(args) })
|
|
2564
|
+
}
|
|
2565
|
+
];
|
|
2566
|
+
var BRACE_PLACEHOLDER = /\{([^{}]*)\}/g;
|
|
2567
|
+
function braceFormatted(receiver, args) {
|
|
2568
|
+
const template = (0, import_values2.literalOf)(receiver);
|
|
2569
|
+
if (template === null) {
|
|
2570
|
+
return (0, import_values2.hole)("value");
|
|
2571
|
+
}
|
|
2572
|
+
const parts = [];
|
|
2573
|
+
let last = 0;
|
|
2574
|
+
let position = 0;
|
|
2575
|
+
for (const match of template.matchAll(BRACE_PLACEHOLDER)) {
|
|
2576
|
+
parts.push((0, import_values2.text)(template.slice(last, match.index)));
|
|
2577
|
+
const argument = args[position];
|
|
2578
|
+
const placeholder = match[1] ?? "";
|
|
2579
|
+
parts.push(
|
|
2580
|
+
argument === void 0 ? (0, import_values2.hole)(
|
|
2581
|
+
placeholder === "" ? "value" : placeholder.split(/[!:]/)[0] ?? "value"
|
|
2582
|
+
) : (0, import_values2.concat)([argument])
|
|
2305
2583
|
);
|
|
2584
|
+
position += 1;
|
|
2585
|
+
last = match.index + match[0].length;
|
|
2306
2586
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2587
|
+
parts.push((0, import_values2.text)(template.slice(last)));
|
|
2588
|
+
return (0, import_values2.concat)(parts);
|
|
2589
|
+
}
|
|
2590
|
+
var pythonRows = [
|
|
2591
|
+
...operatorRows,
|
|
2592
|
+
...stringRows,
|
|
2593
|
+
...sequenceRows,
|
|
2594
|
+
...calleeRows
|
|
2595
|
+
];
|
|
2596
|
+
|
|
2597
|
+
// src/values/evaluator.ts
|
|
2598
|
+
var evaluators = /* @__PURE__ */ new WeakMap();
|
|
2599
|
+
var withoutFacts = /* @__PURE__ */ new WeakMap();
|
|
2600
|
+
var modulesByTree = /* @__PURE__ */ new WeakMap();
|
|
2601
|
+
function bindEvaluator(db, nodes) {
|
|
2602
|
+
for (const entry of nodes.files) {
|
|
2603
|
+
modulesByTree.set(entry.root.tree, entry.module);
|
|
2309
2604
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2605
|
+
evaluators.set(
|
|
2606
|
+
db,
|
|
2607
|
+
new import_values3.Evaluator(
|
|
2608
|
+
pythonLowering({
|
|
2609
|
+
context: contextOver(db, nodes),
|
|
2610
|
+
originOf: calleeOrigin,
|
|
2611
|
+
rows: pythonRows
|
|
2612
|
+
})
|
|
2613
|
+
)
|
|
2614
|
+
);
|
|
2615
|
+
}
|
|
2616
|
+
function evaluatedValue(node, db) {
|
|
2617
|
+
return (0, import_values3.force)(evaluatorFor(node, db).evaluate(node));
|
|
2618
|
+
}
|
|
2619
|
+
function stringValueOf(node, db) {
|
|
2620
|
+
return (0, import_values3.literalOf)(evaluatedValue(node, db));
|
|
2621
|
+
}
|
|
2622
|
+
function evaluatorFor(node, db) {
|
|
2623
|
+
const bound = db === void 0 ? void 0 : evaluators.get(db);
|
|
2624
|
+
if (bound !== void 0) {
|
|
2625
|
+
return bound;
|
|
2626
|
+
}
|
|
2627
|
+
const tree = node.tree;
|
|
2628
|
+
let local = withoutFacts.get(tree);
|
|
2629
|
+
if (local === void 0) {
|
|
2630
|
+
local = new import_values3.Evaluator(
|
|
2631
|
+
pythonLowering({
|
|
2632
|
+
context: null,
|
|
2633
|
+
originOf: calleeOrigin,
|
|
2634
|
+
rows: pythonRows
|
|
2319
2635
|
})
|
|
2320
2636
|
);
|
|
2637
|
+
withoutFacts.set(tree, local);
|
|
2321
2638
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
return { kind: "function", node: peeled };
|
|
2639
|
+
return local;
|
|
2640
|
+
}
|
|
2641
|
+
function moduleOf(node) {
|
|
2642
|
+
const tree = node.tree;
|
|
2643
|
+
let module2 = modulesByTree.get(tree);
|
|
2644
|
+
if (module2 === void 0) {
|
|
2645
|
+
module2 = bindModule(tree.rootNode);
|
|
2646
|
+
modulesByTree.set(tree, module2);
|
|
2331
2647
|
}
|
|
2332
|
-
return
|
|
2648
|
+
return module2;
|
|
2333
2649
|
}
|
|
2334
|
-
function
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2650
|
+
function calleeOrigin(callee) {
|
|
2651
|
+
return originOf(callee, moduleOf(callee));
|
|
2652
|
+
}
|
|
2653
|
+
function contextOver(db, nodes) {
|
|
2654
|
+
const filesByRoot = /* @__PURE__ */ new Map();
|
|
2655
|
+
const rootsByFile = /* @__PURE__ */ new Map();
|
|
2656
|
+
for (const entry of nodes.files) {
|
|
2657
|
+
filesByRoot.set(entry.root.id, entry);
|
|
2658
|
+
rootsByFile.set(entry.file, entry.root);
|
|
2338
2659
|
}
|
|
2339
|
-
const
|
|
2340
|
-
|
|
2341
|
-
if (
|
|
2342
|
-
|
|
2343
|
-
continue;
|
|
2660
|
+
const keyOf2 = (node) => {
|
|
2661
|
+
const entry = filesByRoot.get(node.tree.rootNode.id);
|
|
2662
|
+
if (entry === void 0) {
|
|
2663
|
+
return null;
|
|
2344
2664
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2665
|
+
return readKey(entry.file, node, enclosingFunction(node));
|
|
2666
|
+
};
|
|
2667
|
+
return {
|
|
2668
|
+
writtenTo: (node) => {
|
|
2669
|
+
const key = keyOf2(node);
|
|
2670
|
+
if (key === null) {
|
|
2671
|
+
return null;
|
|
2672
|
+
}
|
|
2673
|
+
const answer = writtenValueOf(db, key);
|
|
2674
|
+
return answer === null ? null : (0, import_resolution2.nodeOfKey)(rootsByFile, answer);
|
|
2675
|
+
},
|
|
2676
|
+
callable: (call) => {
|
|
2677
|
+
const callee = field(call, "function");
|
|
2678
|
+
const key = callee === null ? null : keyOf2(callee);
|
|
2679
|
+
if (key === null) {
|
|
2680
|
+
return null;
|
|
2349
2681
|
}
|
|
2682
|
+
resolveCalls(db, [key]);
|
|
2683
|
+
const resolved = db.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
2684
|
+
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2685
|
+
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
2350
2686
|
}
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
// src/paths/raisedResponses.ts
|
|
2691
|
+
var NESTED_DEFINITION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
2692
|
+
function raisedResponses(body, options) {
|
|
2693
|
+
if (body === null || options.calls.length === 0) {
|
|
2694
|
+
return [];
|
|
2351
2695
|
}
|
|
2352
|
-
|
|
2696
|
+
const found = [];
|
|
2697
|
+
const walk = (node) => {
|
|
2698
|
+
for (const child of node.namedChildren) {
|
|
2699
|
+
if (child === null || NESTED_DEFINITION_TYPES2.has(child.type)) {
|
|
2700
|
+
continue;
|
|
2701
|
+
}
|
|
2702
|
+
const response = raisedResponseOf(child, options);
|
|
2703
|
+
if (response !== null) {
|
|
2704
|
+
found.push(response);
|
|
2705
|
+
}
|
|
2706
|
+
walk(child);
|
|
2707
|
+
}
|
|
2708
|
+
};
|
|
2709
|
+
walk(body);
|
|
2710
|
+
return found;
|
|
2353
2711
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2712
|
+
var ENDING_EXPRESSION = {
|
|
2713
|
+
raise_statement: (statement) => statement.namedChildren[0] ?? null,
|
|
2714
|
+
expression_statement: (statement) => {
|
|
2715
|
+
const only = statement.namedChildren[0];
|
|
2716
|
+
return only?.type === "call" ? only : null;
|
|
2717
|
+
}
|
|
2718
|
+
};
|
|
2719
|
+
function raisedResponseOf(statement, options) {
|
|
2720
|
+
const expression = ENDING_EXPRESSION[statement.type]?.(statement) ?? null;
|
|
2721
|
+
if (expression === null) {
|
|
2722
|
+
return null;
|
|
2723
|
+
}
|
|
2724
|
+
const thrownByCall = statement.type !== "raise_statement";
|
|
2725
|
+
const call = expression.type === "call" ? expression : null;
|
|
2726
|
+
const callee = calleeOf(expression, call);
|
|
2727
|
+
const declared = declaredCallFor(callee, options);
|
|
2728
|
+
if (declared === void 0 && thrownByCall) {
|
|
2729
|
+
return null;
|
|
2362
2730
|
}
|
|
2363
2731
|
return {
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2732
|
+
statement,
|
|
2733
|
+
thrownByCall,
|
|
2734
|
+
terminal: terminalOf(statement, callee, call, declared, options.facts)
|
|
2367
2735
|
};
|
|
2368
2736
|
}
|
|
2369
|
-
function
|
|
2370
|
-
return
|
|
2371
|
-
node,
|
|
2372
|
-
["function", "arguments"],
|
|
2373
|
-
OPAQUE,
|
|
2374
|
-
(callee, argumentList) => {
|
|
2375
|
-
const peeledCallee = peelValue(callee);
|
|
2376
|
-
const args = argumentList.type === "argument_list" ? argumentsOf(argumentList) : [];
|
|
2377
|
-
const isMethod2 = peeledCallee.type === "attribute";
|
|
2378
|
-
return {
|
|
2379
|
-
kind: "call",
|
|
2380
|
-
callee: {
|
|
2381
|
-
receiver: isMethod2 ? field(peeledCallee, "object") : null,
|
|
2382
|
-
name: calleeName(peeledCallee),
|
|
2383
|
-
origin: () => originOf2(peeledCallee)
|
|
2384
|
-
},
|
|
2385
|
-
args,
|
|
2386
|
-
constructs: false
|
|
2387
|
-
};
|
|
2388
|
-
}
|
|
2389
|
-
);
|
|
2737
|
+
function calleeOf(expression, call) {
|
|
2738
|
+
return call === null ? expression : field(call, "function") ?? expression;
|
|
2390
2739
|
}
|
|
2391
|
-
function
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
if (callee.type === "attribute") {
|
|
2396
|
-
return field(callee, "attribute")?.text ?? null;
|
|
2740
|
+
function declaredCallFor(callee, options) {
|
|
2741
|
+
const origin = originOf(callee, options.module);
|
|
2742
|
+
if (origin === null) {
|
|
2743
|
+
return void 0;
|
|
2397
2744
|
}
|
|
2398
|
-
|
|
2745
|
+
const qualified = `${origin.module}.${origin.name}`;
|
|
2746
|
+
return options.calls.find((call) => call.callee === qualified);
|
|
2399
2747
|
}
|
|
2400
|
-
function
|
|
2401
|
-
return
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2748
|
+
function terminalOf(statement, callee, call, declared, facts) {
|
|
2749
|
+
return {
|
|
2750
|
+
kind: "throw",
|
|
2751
|
+
statusCode: declared === void 0 ? null : statusOf(declared, call, facts),
|
|
2752
|
+
body: null,
|
|
2753
|
+
exceptionType: lastSegmentOf(declared?.callee ?? callee.text),
|
|
2754
|
+
message: call === null ? null : writtenMessageOf(call),
|
|
2755
|
+
...declared === void 0 ? {} : { producesResponse: true },
|
|
2756
|
+
component: null,
|
|
2757
|
+
renderTree: null,
|
|
2758
|
+
delegateTarget: null,
|
|
2759
|
+
emitEvent: null,
|
|
2760
|
+
location: rangeOf(statement)
|
|
2761
|
+
};
|
|
2412
2762
|
}
|
|
2413
|
-
function
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2763
|
+
function lastSegmentOf(dotted) {
|
|
2764
|
+
const segment = dotted.split(".").pop();
|
|
2765
|
+
return segment === void 0 || segment === "" ? null : segment;
|
|
2766
|
+
}
|
|
2767
|
+
function statusOf(declared, call, facts) {
|
|
2768
|
+
const written = call === null ? null : statusArgumentOf(declared, call);
|
|
2769
|
+
if (written === null) {
|
|
2770
|
+
return declared.defaultStatusCode === void 0 ? null : { type: "literal", value: declared.defaultStatusCode };
|
|
2417
2771
|
}
|
|
2418
|
-
|
|
2419
|
-
|
|
2772
|
+
const value = (0, import_values5.constantOf)(evaluatedValue(written, facts));
|
|
2773
|
+
if (typeof value === "number") {
|
|
2774
|
+
return { type: "literal", value };
|
|
2775
|
+
}
|
|
2776
|
+
return { type: "dynamic", sourceText: written.text };
|
|
2777
|
+
}
|
|
2778
|
+
function argumentsOf2(call) {
|
|
2779
|
+
return (field(call, "arguments")?.namedChildren ?? []).filter(
|
|
2780
|
+
(child) => child !== null
|
|
2420
2781
|
);
|
|
2421
2782
|
}
|
|
2422
|
-
function
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2783
|
+
function statusArgumentOf(declared, call) {
|
|
2784
|
+
const args = argumentsOf2(call);
|
|
2785
|
+
const keyed = args.find(
|
|
2786
|
+
(arg) => arg.type === "keyword_argument" && field(arg, "name")?.text === declared.statusKeyword
|
|
2787
|
+
);
|
|
2788
|
+
if (keyed !== void 0) {
|
|
2789
|
+
return field(keyed, "value");
|
|
2426
2790
|
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
function fieldOf(node) {
|
|
2430
|
-
if (node.type === "dictionary_splat") {
|
|
2431
|
-
const inner = children(node)[0];
|
|
2432
|
-
return inner === void 0 ? [] : [{ kind: "spread", node: inner }];
|
|
2791
|
+
if (declared.statusArgument === void 0) {
|
|
2792
|
+
return null;
|
|
2433
2793
|
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2794
|
+
const positional = args.filter((arg) => arg.type !== "keyword_argument");
|
|
2795
|
+
return positional[declared.statusArgument] ?? null;
|
|
2796
|
+
}
|
|
2797
|
+
function writtenMessageOf(call) {
|
|
2798
|
+
for (const arg of argumentsOf2(call)) {
|
|
2799
|
+
const value = arg.type === "keyword_argument" ? field(arg, "value") : arg;
|
|
2800
|
+
const literal = value === null ? null : stringLiteralValue(value);
|
|
2801
|
+
if (literal !== null) {
|
|
2802
|
+
return literal;
|
|
2803
|
+
}
|
|
2436
2804
|
}
|
|
2437
|
-
return
|
|
2438
|
-
const name = stringLiteralValue(key);
|
|
2439
|
-
return name === null ? [{ kind: "computed", name: key, value }] : [{ kind: "field", name, value }];
|
|
2440
|
-
});
|
|
2805
|
+
return null;
|
|
2441
2806
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
kind: "return",
|
|
2450
|
-
value: children(node)[0] ?? null
|
|
2451
|
-
}),
|
|
2452
|
-
try_statement: (node) => ({
|
|
2453
|
-
kind: "block",
|
|
2454
|
-
body: [
|
|
2455
|
-
...blockStatements2(field(node, "body")),
|
|
2456
|
-
...children(node).flatMap(
|
|
2457
|
-
(clause) => clause.type === "finally_clause" ? blockStatements2(children(clause)[0] ?? null) : []
|
|
2458
|
-
)
|
|
2459
|
-
]
|
|
2460
|
-
}),
|
|
2461
|
-
with_statement: (node) => ({
|
|
2462
|
-
kind: "block",
|
|
2463
|
-
body: blockStatements2(field(node, "body"))
|
|
2464
|
-
})
|
|
2807
|
+
|
|
2808
|
+
// src/paths/returnedShape.ts
|
|
2809
|
+
var MAX_DEPTH = 6;
|
|
2810
|
+
var SCALARS = {
|
|
2811
|
+
none: { type: "null" },
|
|
2812
|
+
true: { type: "boolean" },
|
|
2813
|
+
false: { type: "boolean" }
|
|
2465
2814
|
};
|
|
2466
|
-
function
|
|
2467
|
-
|
|
2468
|
-
return lower === void 0 ? { kind: "opaque" } : lower(node);
|
|
2815
|
+
function children2(node) {
|
|
2816
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
2469
2817
|
}
|
|
2470
|
-
function
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2473
|
-
|
|
2818
|
+
function recordShape(node, depth) {
|
|
2819
|
+
const properties = {};
|
|
2820
|
+
let everyKeyWritten = true;
|
|
2821
|
+
for (const pair of children2(node)) {
|
|
2822
|
+
if (pair.type !== "pair") {
|
|
2823
|
+
everyKeyWritten = false;
|
|
2824
|
+
continue;
|
|
2825
|
+
}
|
|
2826
|
+
const key = field(pair, "key");
|
|
2827
|
+
const value = field(pair, "value");
|
|
2828
|
+
if (key === null || value === null || key.type !== "string") {
|
|
2829
|
+
everyKeyWritten = false;
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
properties[key.text.slice(1, -1)] = shapeOfReturned(value, depth + 1);
|
|
2474
2833
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2834
|
+
return everyKeyWritten && Object.keys(properties).length > 0 ? { type: "record", properties } : { type: "dictionary", values: { type: "unknown" } };
|
|
2835
|
+
}
|
|
2836
|
+
function shapeOfReturned(node, depth = 0) {
|
|
2837
|
+
if (depth > MAX_DEPTH) {
|
|
2838
|
+
return { type: "unknown" };
|
|
2839
|
+
}
|
|
2840
|
+
const scalar = SCALARS[node.type];
|
|
2841
|
+
if (scalar !== void 0) {
|
|
2842
|
+
return scalar;
|
|
2843
|
+
}
|
|
2844
|
+
if (node.type === "string") {
|
|
2845
|
+
return { type: "literal", value: node.text.slice(1, -1) };
|
|
2846
|
+
}
|
|
2847
|
+
if (node.type === "integer") {
|
|
2848
|
+
const value = Number.parseInt(node.text, 10);
|
|
2849
|
+
return Number.isNaN(value) ? { type: "integer" } : { type: "literal", value };
|
|
2850
|
+
}
|
|
2851
|
+
if (node.type === "float") {
|
|
2852
|
+
return { type: "number" };
|
|
2853
|
+
}
|
|
2854
|
+
if (node.type === "dictionary") {
|
|
2855
|
+
return recordShape(node, depth);
|
|
2856
|
+
}
|
|
2857
|
+
if (node.type === "list" || node.type === "tuple" || node.type === "set") {
|
|
2858
|
+
const first = children2(node)[0];
|
|
2859
|
+
return {
|
|
2860
|
+
type: "array",
|
|
2861
|
+
items: first === void 0 ? { type: "unknown" } : shapeOfReturned(first, depth + 1)
|
|
2862
|
+
};
|
|
2477
2863
|
}
|
|
2478
|
-
if (
|
|
2479
|
-
|
|
2480
|
-
return assignment(
|
|
2481
|
-
inner,
|
|
2482
|
-
operator === null ? null : operator.text.slice(0, -1)
|
|
2483
|
-
);
|
|
2864
|
+
if (node.type === "identifier" || node.type === "attribute") {
|
|
2865
|
+
return { type: "ref", name: node.text };
|
|
2484
2866
|
}
|
|
2485
|
-
return {
|
|
2486
|
-
}
|
|
2487
|
-
function assignment(node, operator) {
|
|
2488
|
-
return fromFields(
|
|
2489
|
-
node,
|
|
2490
|
-
["left", "right"],
|
|
2491
|
-
OPAQUE,
|
|
2492
|
-
(target, value) => {
|
|
2493
|
-
if (target.type === "pattern_list" || target.type === "tuple_pattern") {
|
|
2494
|
-
return {
|
|
2495
|
-
kind: "declare",
|
|
2496
|
-
bindings: patternNames(target).map((name) => ({ name, value: null }))
|
|
2497
|
-
};
|
|
2498
|
-
}
|
|
2499
|
-
return { kind: "assign", target, operator, value };
|
|
2500
|
-
}
|
|
2501
|
-
);
|
|
2502
|
-
}
|
|
2503
|
-
function patternNames(pattern) {
|
|
2504
|
-
return children(pattern).flatMap(
|
|
2505
|
-
(child) => child.type === "identifier" ? [child.text] : patternNames(child)
|
|
2506
|
-
);
|
|
2867
|
+
return { type: "unknown" };
|
|
2507
2868
|
}
|
|
2508
|
-
function
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2869
|
+
function returnedBodyShape(statement) {
|
|
2870
|
+
const returned = statement.namedChildren[0];
|
|
2871
|
+
if (returned == null) {
|
|
2872
|
+
return null;
|
|
2873
|
+
}
|
|
2874
|
+
const value = returned.type === "expression_list" ? children2(returned)[0] : returned;
|
|
2875
|
+
if (value === void 0) {
|
|
2876
|
+
return null;
|
|
2877
|
+
}
|
|
2878
|
+
const shape = shapeOfReturned(value);
|
|
2879
|
+
return shape.type === "unknown" ? null : shape;
|
|
2519
2880
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
)
|
|
2526
|
-
if (next === void 0) {
|
|
2881
|
+
|
|
2882
|
+
// src/rawSql.ts
|
|
2883
|
+
var import_ir_core = require("@suss/ir-core");
|
|
2884
|
+
var import_sql = require("@suss/sql");
|
|
2885
|
+
function rawSqlEffects(calls, options) {
|
|
2886
|
+
if (options.patterns.length === 0) {
|
|
2527
2887
|
return [];
|
|
2528
2888
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
function blockStatements2(block) {
|
|
2535
|
-
return block === null ? [] : children(block);
|
|
2536
|
-
}
|
|
2537
|
-
function isRoot(node) {
|
|
2538
|
-
return ROOT_TYPES.has(node.type);
|
|
2539
|
-
}
|
|
2540
|
-
function isPathStatement(node, parent) {
|
|
2541
|
-
return parent.type === "module" || parent.type === "block" || node.type === "elif_clause";
|
|
2542
|
-
}
|
|
2543
|
-
function siteOf(node) {
|
|
2544
|
-
const path6 = [];
|
|
2545
|
-
let current = node;
|
|
2546
|
-
for (; ; ) {
|
|
2547
|
-
const parent = current.parent;
|
|
2548
|
-
if (parent === null) {
|
|
2549
|
-
return null;
|
|
2889
|
+
const effects = [];
|
|
2890
|
+
for (const call of calls) {
|
|
2891
|
+
const pattern = patternFor(call, options);
|
|
2892
|
+
if (pattern === void 0) {
|
|
2893
|
+
continue;
|
|
2550
2894
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2895
|
+
const statement = statementIn(call);
|
|
2896
|
+
if (statement === null) {
|
|
2897
|
+
continue;
|
|
2553
2898
|
}
|
|
2554
|
-
|
|
2555
|
-
|
|
2899
|
+
for (const access of (0, import_sql.readSqlAccess)(statement, {
|
|
2900
|
+
dialect: pattern.storageSystem
|
|
2901
|
+
})) {
|
|
2902
|
+
effects.push({
|
|
2903
|
+
type: "interaction",
|
|
2904
|
+
binding: (0, import_ir_core.storageBinding)({
|
|
2905
|
+
recognition: `python-${pattern.module}`,
|
|
2906
|
+
storageSystem: pattern.storageSystem,
|
|
2907
|
+
scope: pattern.module,
|
|
2908
|
+
container: access.table
|
|
2909
|
+
}),
|
|
2910
|
+
callee: field(call, "function")?.text ?? "",
|
|
2911
|
+
interaction: {
|
|
2912
|
+
class: "storage-access",
|
|
2913
|
+
kind: access.kind,
|
|
2914
|
+
fields: access.fields,
|
|
2915
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
2916
|
+
operation: field(call, "function")?.text ?? ""
|
|
2917
|
+
}
|
|
2918
|
+
});
|
|
2556
2919
|
}
|
|
2557
|
-
current = parent;
|
|
2558
2920
|
}
|
|
2921
|
+
return effects;
|
|
2559
2922
|
}
|
|
2560
|
-
function
|
|
2561
|
-
|
|
2562
|
-
|
|
2923
|
+
function patternFor(call, options) {
|
|
2924
|
+
const callee = field(call, "function");
|
|
2925
|
+
if (callee === null || callee.type !== "identifier") {
|
|
2926
|
+
return void 0;
|
|
2563
2927
|
}
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
return null;
|
|
2568
|
-
}
|
|
2569
|
-
return { parameters: parameterShapes(node), body: { expression: body } };
|
|
2928
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
2929
|
+
if (from === void 0) {
|
|
2930
|
+
return void 0;
|
|
2570
2931
|
}
|
|
2571
|
-
|
|
2932
|
+
const module2 = String(from[1]);
|
|
2933
|
+
return options.patterns.find(
|
|
2934
|
+
(pattern) => pattern.module === module2 && pattern.functions.includes(callee.text)
|
|
2935
|
+
);
|
|
2936
|
+
}
|
|
2937
|
+
function statementIn(call) {
|
|
2938
|
+
const args = field(call, "arguments");
|
|
2939
|
+
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
2940
|
+
if (first === null || first.type !== "string") {
|
|
2572
2941
|
return null;
|
|
2573
2942
|
}
|
|
2574
|
-
const
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
function isMethod(fn) {
|
|
2581
|
-
const owner = fn.parent?.type === "decorated_definition" ? fn.parent.parent : fn.parent;
|
|
2582
|
-
return owner?.type === "block" && owner.parent?.type === "class_definition";
|
|
2583
|
-
}
|
|
2584
|
-
function namesMutatedInNestedFunctions(root, readingMethods) {
|
|
2585
|
-
const mutated = /* @__PURE__ */ new Set();
|
|
2586
|
-
const visit = (node, nested) => {
|
|
2587
|
-
const name = writtenNameOf(node, readingMethods);
|
|
2588
|
-
if (nested && name !== null) {
|
|
2589
|
-
mutated.add(name);
|
|
2943
|
+
const parts = [""];
|
|
2944
|
+
for (const child of first.namedChildren) {
|
|
2945
|
+
if (child === null) {
|
|
2946
|
+
continue;
|
|
2590
2947
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2948
|
+
if (child.type === "string_content") {
|
|
2949
|
+
parts[parts.length - 1] += stringContentValue(child);
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
if (child.type === "interpolation") {
|
|
2953
|
+
parts.push("");
|
|
2593
2954
|
}
|
|
2594
|
-
};
|
|
2595
|
-
for (const child of children(root)) {
|
|
2596
|
-
visit(child, isRoot(child));
|
|
2597
2955
|
}
|
|
2598
|
-
|
|
2956
|
+
const statement = (0, import_sql.sqlFromParts)(parts);
|
|
2957
|
+
return statement.trim() === "" ? null : statement;
|
|
2599
2958
|
}
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2959
|
+
|
|
2960
|
+
// src/storage.ts
|
|
2961
|
+
var import_ir_core2 = require("@suss/ir-core");
|
|
2962
|
+
function receiverCall(call) {
|
|
2963
|
+
const callee = field(call, "function");
|
|
2964
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2965
|
+
return null;
|
|
2604
2966
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
if (receiver?.type === "identifier" && method !== null && !readingMethods.has(method.text)) {
|
|
2613
|
-
return receiver.text;
|
|
2614
|
-
}
|
|
2967
|
+
const object = field(callee, "object");
|
|
2968
|
+
return object !== null && object.type === "call" ? object : null;
|
|
2969
|
+
}
|
|
2970
|
+
function methodName(call) {
|
|
2971
|
+
const callee = field(call, "function");
|
|
2972
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2973
|
+
return callee?.text ?? "";
|
|
2615
2974
|
}
|
|
2616
|
-
return
|
|
2975
|
+
return field(callee, "attribute")?.text ?? "";
|
|
2617
2976
|
}
|
|
2618
|
-
|
|
2619
|
-
"
|
|
2620
|
-
"
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
"
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
const
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
continue;
|
|
2636
|
-
}
|
|
2637
|
-
if (node.type === "attribute" && field(node, "attribute")?.id === child.id) {
|
|
2638
|
-
continue;
|
|
2639
|
-
}
|
|
2640
|
-
if (node.type === "keyword_argument" && field(node, "name")?.id === child.id) {
|
|
2641
|
-
continue;
|
|
2642
|
-
}
|
|
2643
|
-
if (DECLARING_PARENTS.has(node.type) || node.type === "assignment" && field(node, "left")?.id === child.id) {
|
|
2644
|
-
declared.add(child.text);
|
|
2645
|
-
continue;
|
|
2646
|
-
}
|
|
2647
|
-
read.add(child.text);
|
|
2977
|
+
function subjectOf(call) {
|
|
2978
|
+
const callee = field(call, "function");
|
|
2979
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2980
|
+
return callee?.text ?? "";
|
|
2981
|
+
}
|
|
2982
|
+
return field(callee, "object")?.text ?? "";
|
|
2983
|
+
}
|
|
2984
|
+
function rootOf(call) {
|
|
2985
|
+
const receiver = receiverCall(call);
|
|
2986
|
+
return receiver === null ? call : rootOf(receiver);
|
|
2987
|
+
}
|
|
2988
|
+
function chainsIn(calls) {
|
|
2989
|
+
const endsWith = /* @__PURE__ */ new Map();
|
|
2990
|
+
for (const call of calls) {
|
|
2991
|
+
const receiver = receiverCall(call);
|
|
2992
|
+
if (receiver === null) {
|
|
2993
|
+
continue;
|
|
2648
2994
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2995
|
+
const start = rootOf(receiver);
|
|
2996
|
+
const kept = endsWith.get(start.id);
|
|
2997
|
+
if (kept === void 0 || call.endIndex > kept.endIndex) {
|
|
2998
|
+
endsWith.set(start.id, call);
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
const chains = [];
|
|
3002
|
+
for (const call of calls) {
|
|
3003
|
+
if (receiverCall(call) !== null) {
|
|
3004
|
+
continue;
|
|
3005
|
+
}
|
|
3006
|
+
const last = endsWith.get(call.id) ?? call;
|
|
3007
|
+
chains.push({
|
|
3008
|
+
root: call,
|
|
3009
|
+
last,
|
|
3010
|
+
subject: subjectOf(call),
|
|
3011
|
+
operation: methodName(last),
|
|
3012
|
+
between: calls.filter(
|
|
3013
|
+
(other) => other !== call && other !== last && other.startIndex >= call.startIndex && other.endIndex <= last.endIndex
|
|
3014
|
+
)
|
|
3015
|
+
});
|
|
3016
|
+
}
|
|
3017
|
+
return chains;
|
|
2652
3018
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
if (chain === null) {
|
|
2658
|
-
return null;
|
|
3019
|
+
function importedQueryFunction(options, chain) {
|
|
3020
|
+
const callee = field(chain.root, "function");
|
|
3021
|
+
if (callee === null || callee.type !== "identifier") {
|
|
3022
|
+
return void 0;
|
|
2659
3023
|
}
|
|
2660
|
-
const [
|
|
2661
|
-
if (
|
|
3024
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
3025
|
+
if (from === void 0) {
|
|
3026
|
+
return void 0;
|
|
3027
|
+
}
|
|
3028
|
+
return options.patterns.find(
|
|
3029
|
+
(pattern) => String(from[1]) === pattern.module && (pattern.queryFunctions ?? []).includes(String(from[2]))
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
function receiverName(chain) {
|
|
3033
|
+
const callee = field(chain.root, "function");
|
|
3034
|
+
if (callee === null || callee.type !== "attribute") {
|
|
2662
3035
|
return null;
|
|
2663
3036
|
}
|
|
2664
|
-
const
|
|
2665
|
-
|
|
2666
|
-
|
|
3037
|
+
const object = field(callee, "object");
|
|
3038
|
+
return object !== null && object.type === "identifier" ? object.text : null;
|
|
3039
|
+
}
|
|
3040
|
+
function typeNameOf(annotation) {
|
|
3041
|
+
if (annotation.type === "type" && annotation.namedChildren[0]) {
|
|
3042
|
+
return typeNameOf(annotation.namedChildren[0]);
|
|
2667
3043
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
return null;
|
|
3044
|
+
if (annotation.type === "identifier") {
|
|
3045
|
+
return annotation.text;
|
|
2671
3046
|
}
|
|
2672
|
-
const
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
return null;
|
|
3047
|
+
const quoted = stringLiteralValue(annotation);
|
|
3048
|
+
if (quoted !== null) {
|
|
3049
|
+
return quoted;
|
|
2676
3050
|
}
|
|
2677
|
-
|
|
3051
|
+
if (annotation.type === "generic_type") {
|
|
3052
|
+
const outer = annotation.namedChildren[0];
|
|
3053
|
+
const first = genericTypeArgs(annotation)[0];
|
|
3054
|
+
if ((outer?.text === "Annotated" || outer?.text === "Optional") && first !== void 0) {
|
|
3055
|
+
return typeNameOf(first);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
return null;
|
|
2678
3059
|
}
|
|
2679
|
-
function
|
|
2680
|
-
if (
|
|
2681
|
-
if (
|
|
3060
|
+
function typeGivenBy(statement, name) {
|
|
3061
|
+
if (statement.type === "assignment") {
|
|
3062
|
+
if (field(statement, "left")?.text !== name) {
|
|
2682
3063
|
return null;
|
|
2683
3064
|
}
|
|
2684
|
-
|
|
3065
|
+
const annotation = field(statement, "type");
|
|
3066
|
+
if (annotation !== null) {
|
|
3067
|
+
return typeNameOf(annotation);
|
|
3068
|
+
}
|
|
3069
|
+
const right = field(statement, "right");
|
|
3070
|
+
const callee = right?.type === "call" ? field(right, "function") : null;
|
|
3071
|
+
return callee?.type === "identifier" ? callee.text : null;
|
|
2685
3072
|
}
|
|
2686
|
-
if (
|
|
2687
|
-
|
|
3073
|
+
if (statement.type === "as_pattern") {
|
|
3074
|
+
const alias = field(statement, "alias");
|
|
3075
|
+
const value = statement.namedChildren[0] ?? null;
|
|
3076
|
+
const callee = value?.type === "call" ? field(value, "function") : null;
|
|
3077
|
+
return alias?.text === name && callee?.type === "identifier" ? callee.text : null;
|
|
2688
3078
|
}
|
|
2689
|
-
|
|
2690
|
-
return unaliasedDotted ? [localName] : binding.module.split(".");
|
|
3079
|
+
return null;
|
|
2691
3080
|
}
|
|
2692
|
-
function
|
|
2693
|
-
const
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
3081
|
+
function declaredTypeName(name, from) {
|
|
3082
|
+
const fn = enclosingFunction(from);
|
|
3083
|
+
if (fn === null) {
|
|
3084
|
+
return null;
|
|
3085
|
+
}
|
|
3086
|
+
const params = field(fn, "parameters");
|
|
3087
|
+
for (const param of params === null ? [] : children(params)) {
|
|
3088
|
+
const info = parameterNameAndType(param);
|
|
3089
|
+
if (info?.name === name && info.typeNode !== null) {
|
|
3090
|
+
return typeNameOf(info.typeNode);
|
|
2700
3091
|
}
|
|
2701
|
-
names.unshift(attribute.text);
|
|
2702
|
-
current = peelValue(object);
|
|
2703
3092
|
}
|
|
2704
|
-
|
|
3093
|
+
const visit = (node) => {
|
|
3094
|
+
const given = typeGivenBy(node, name);
|
|
3095
|
+
if (given !== null) {
|
|
3096
|
+
return given;
|
|
3097
|
+
}
|
|
3098
|
+
for (const child of children(node)) {
|
|
3099
|
+
if (child.type === "function_definition" || child.type === "lambda") {
|
|
3100
|
+
continue;
|
|
3101
|
+
}
|
|
3102
|
+
const found = visit(child);
|
|
3103
|
+
if (found !== null) {
|
|
3104
|
+
return found;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
2705
3107
|
return null;
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
const scope = module2.scopeFor.get(current.id);
|
|
2713
|
-
if (scope !== void 0) {
|
|
2714
|
-
return scope;
|
|
3108
|
+
};
|
|
3109
|
+
const body = field(fn, "body");
|
|
3110
|
+
for (const statement of body === null ? [] : bodyStatements(body)) {
|
|
3111
|
+
const found = visit(statement);
|
|
3112
|
+
if (found !== null) {
|
|
3113
|
+
return found;
|
|
2715
3114
|
}
|
|
2716
|
-
current = current.parent;
|
|
2717
3115
|
}
|
|
2718
|
-
return
|
|
3116
|
+
return null;
|
|
2719
3117
|
}
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
{
|
|
2725
|
-
kind: "operator",
|
|
2726
|
-
operator: "+",
|
|
2727
|
-
arity: 2,
|
|
2728
|
-
apply: ([a, b]) => (0, import_values3.plus)((0, import_values3.operand)(a), (0, import_values3.operand)(b))
|
|
2729
|
-
},
|
|
2730
|
-
{
|
|
2731
|
-
kind: "operator",
|
|
2732
|
-
operator: "%",
|
|
2733
|
-
arity: 2,
|
|
2734
|
-
apply: ([a, b], contentOf) => (0, import_values3.percentFormatted)((0, import_values3.operand)(a), (0, import_values3.formatArguments)(contentOf((0, import_values3.operand)(b))))
|
|
2735
|
-
},
|
|
2736
|
-
{
|
|
2737
|
-
kind: "operator",
|
|
2738
|
-
operator: "==",
|
|
2739
|
-
arity: 2,
|
|
2740
|
-
apply: ([a, b]) => (0, import_values3.equals)((0, import_values3.operand)(a), (0, import_values3.operand)(b))
|
|
2741
|
-
},
|
|
2742
|
-
{
|
|
2743
|
-
kind: "operator",
|
|
2744
|
-
operator: "!=",
|
|
2745
|
-
arity: 2,
|
|
2746
|
-
apply: ([a, b]) => (0, import_values3.negated)((0, import_values3.equals)((0, import_values3.operand)(a), (0, import_values3.operand)(b)))
|
|
2747
|
-
},
|
|
2748
|
-
{
|
|
2749
|
-
kind: "operator",
|
|
2750
|
-
operator: "is",
|
|
2751
|
-
arity: 2,
|
|
2752
|
-
apply: ([a, b]) => (0, import_values3.equals)((0, import_values3.operand)(a), (0, import_values3.operand)(b))
|
|
2753
|
-
},
|
|
2754
|
-
{
|
|
2755
|
-
kind: "operator",
|
|
2756
|
-
operator: "is not",
|
|
2757
|
-
arity: 2,
|
|
2758
|
-
apply: ([a, b]) => (0, import_values3.negated)((0, import_values3.equals)((0, import_values3.operand)(a), (0, import_values3.operand)(b)))
|
|
2759
|
-
},
|
|
2760
|
-
{
|
|
2761
|
-
kind: "operator",
|
|
2762
|
-
operator: "not",
|
|
2763
|
-
arity: 1,
|
|
2764
|
-
apply: ([a]) => (0, import_values3.negated)((0, import_values3.operand)(a))
|
|
2765
|
-
},
|
|
2766
|
-
{
|
|
2767
|
-
kind: "operator",
|
|
2768
|
-
operator: "or",
|
|
2769
|
-
arity: 2,
|
|
2770
|
-
apply: ([a, b]) => (0, import_values3.readableFallback)((0, import_values3.operand)(a), (0, import_values3.operand)(b), import_values3.truthOf)
|
|
2771
|
-
},
|
|
2772
|
-
{
|
|
2773
|
-
kind: "operator",
|
|
2774
|
-
operator: "and",
|
|
2775
|
-
arity: 2,
|
|
2776
|
-
apply: ([a, b]) => (0, import_values3.fallback)((0, import_values3.operand)(a), (0, import_values3.operand)(b), (left) => {
|
|
2777
|
-
const truth = (0, import_values3.truthOf)(left);
|
|
2778
|
-
return truth === null ? null : !truth;
|
|
2779
|
-
})
|
|
2780
|
-
}
|
|
2781
|
-
];
|
|
2782
|
-
var stringRows = [
|
|
2783
|
-
{
|
|
2784
|
-
kind: "method",
|
|
2785
|
-
method: "join",
|
|
2786
|
-
on: "string",
|
|
2787
|
-
apply: ({ receiver, args, contentOf }) => ({
|
|
2788
|
-
result: (0, import_values3.joined)(contentOf((0, import_values3.operand)(args[0])), (0, import_values3.operand)(receiver))
|
|
2789
|
-
})
|
|
2790
|
-
},
|
|
2791
|
-
{
|
|
2792
|
-
kind: "method",
|
|
2793
|
-
method: "format",
|
|
2794
|
-
on: "string",
|
|
2795
|
-
apply: ({ receiver, args }) => ({
|
|
2796
|
-
result: braceFormatted((0, import_values3.operand)(receiver), args)
|
|
2797
|
-
})
|
|
2798
|
-
},
|
|
2799
|
-
{
|
|
2800
|
-
kind: "method",
|
|
2801
|
-
method: "strip",
|
|
2802
|
-
on: "string",
|
|
2803
|
-
apply: ({ receiver, args }) => ({
|
|
2804
|
-
result: args.length === 0 ? (0, import_values3.stripped)((0, import_values3.operand)(receiver)) : (0, import_values3.hole)("value")
|
|
2805
|
-
})
|
|
2806
|
-
},
|
|
2807
|
-
{
|
|
2808
|
-
kind: "method",
|
|
2809
|
-
method: "rstrip",
|
|
2810
|
-
on: "string",
|
|
2811
|
-
apply: ({ receiver, args }) => ({
|
|
2812
|
-
result: args.length === 0 ? (0, import_values3.stripped)((0, import_values3.operand)(receiver), "end") : (0, import_values3.hole)("value")
|
|
2813
|
-
})
|
|
2814
|
-
},
|
|
2815
|
-
{
|
|
2816
|
-
kind: "method",
|
|
2817
|
-
method: "lstrip",
|
|
2818
|
-
on: "string",
|
|
2819
|
-
apply: ({ receiver, args }) => ({
|
|
2820
|
-
result: args.length === 0 ? (0, import_values3.stripped)((0, import_values3.operand)(receiver), "start") : (0, import_values3.hole)("value")
|
|
2821
|
-
})
|
|
3118
|
+
function typedReceiverPattern(options, chain) {
|
|
3119
|
+
const receiver = receiverName(chain);
|
|
3120
|
+
if (receiver === null) {
|
|
3121
|
+
return void 0;
|
|
2822
3122
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
kind: "method",
|
|
2827
|
-
method: "append",
|
|
2828
|
-
on,
|
|
2829
|
-
apply: ({ receiver, args }) => ({
|
|
2830
|
-
result: (0, import_values3.constant)(null),
|
|
2831
|
-
receiver: (0, import_values3.appended)((0, import_values3.operand)(receiver), args)
|
|
2832
|
-
})
|
|
2833
|
-
},
|
|
2834
|
-
{
|
|
2835
|
-
kind: "method",
|
|
2836
|
-
method: "extend",
|
|
2837
|
-
on,
|
|
2838
|
-
apply: ({ receiver, args, contentOf }) => ({
|
|
2839
|
-
result: (0, import_values3.constant)(null),
|
|
2840
|
-
receiver: (0, import_values3.extended)((0, import_values3.operand)(receiver), contentOf((0, import_values3.operand)(args[0])))
|
|
2841
|
-
})
|
|
3123
|
+
const typeName = declaredTypeName(receiver, chain.root);
|
|
3124
|
+
if (typeName === null) {
|
|
3125
|
+
return void 0;
|
|
2842
3126
|
}
|
|
2843
|
-
]);
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
kind: "callee",
|
|
2847
|
-
origin: { module: "builtins", name: "str" },
|
|
2848
|
-
apply: ({ args }) => ({ result: (0, import_values3.concat)([(0, import_values3.operand)(args[0])]) })
|
|
2849
|
-
},
|
|
2850
|
-
{
|
|
2851
|
-
kind: "callee",
|
|
2852
|
-
origin: { module: "os.path", name: "join" },
|
|
2853
|
-
apply: ({ args }) => ({ result: (0, import_values3.joinedPath)(args) })
|
|
2854
|
-
},
|
|
2855
|
-
{
|
|
2856
|
-
kind: "callee",
|
|
2857
|
-
origin: { module: "os", name: "getenv" },
|
|
2858
|
-
apply: ({ args }) => ({ result: (0, import_values3.environmentRead)(args) })
|
|
2859
|
-
},
|
|
2860
|
-
{
|
|
2861
|
-
kind: "callee",
|
|
2862
|
-
origin: { module: "os.environ", name: "get" },
|
|
2863
|
-
apply: ({ args }) => ({ result: (0, import_values3.environmentRead)(args) })
|
|
3127
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${typeName}`);
|
|
3128
|
+
if (from === void 0) {
|
|
3129
|
+
return void 0;
|
|
2864
3130
|
}
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3131
|
+
return options.patterns.find(
|
|
3132
|
+
(pattern) => String(from[1]) === pattern.module && pattern.queryTypes.includes(String(from[2]))
|
|
3133
|
+
);
|
|
3134
|
+
}
|
|
3135
|
+
function fieldsOf(chain, valueMethods) {
|
|
3136
|
+
const args = field(chain.root, "arguments");
|
|
3137
|
+
const named = [];
|
|
3138
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
3139
|
+
if (argument.type === "keyword_argument") {
|
|
3140
|
+
const name = field(argument, "name");
|
|
3141
|
+
if (name !== null) {
|
|
3142
|
+
named.push(name.text);
|
|
3143
|
+
}
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
if (argument.type === "attribute") {
|
|
3147
|
+
const property = field(argument, "attribute");
|
|
3148
|
+
if (property !== null) {
|
|
3149
|
+
named.push(property.text);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
2871
3152
|
}
|
|
2872
|
-
const
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
parts.push((0, import_values3.text)(template.slice(last, match.index)));
|
|
2877
|
-
const argument = args[position];
|
|
2878
|
-
const placeholder = match[1] ?? "";
|
|
2879
|
-
parts.push(
|
|
2880
|
-
argument === void 0 ? (0, import_values3.hole)(
|
|
2881
|
-
placeholder === "" ? "value" : placeholder.split(/[!:]/)[0] ?? "value"
|
|
2882
|
-
) : (0, import_values3.concat)([argument])
|
|
2883
|
-
);
|
|
2884
|
-
position += 1;
|
|
2885
|
-
last = match.index + match[0].length;
|
|
3153
|
+
for (const call of laterCalls(chain)) {
|
|
3154
|
+
if (valueMethods.includes(methodNameOf(call))) {
|
|
3155
|
+
named.push(...keywordNames(call));
|
|
3156
|
+
}
|
|
2886
3157
|
}
|
|
2887
|
-
|
|
2888
|
-
return (0, import_values3.concat)(parts);
|
|
3158
|
+
return [...new Set(named)];
|
|
2889
3159
|
}
|
|
2890
|
-
|
|
2891
|
-
...
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
3160
|
+
function laterCalls(chain) {
|
|
3161
|
+
return [...chain.between, chain.last].filter((call) => call !== chain.root);
|
|
3162
|
+
}
|
|
3163
|
+
function methodNameOf(call) {
|
|
3164
|
+
const callee = field(call, "function");
|
|
3165
|
+
return callee?.type === "attribute" ? field(callee, "attribute")?.text ?? "" : callee?.text ?? "";
|
|
3166
|
+
}
|
|
3167
|
+
function keywordNames(call) {
|
|
3168
|
+
const args = field(call, "arguments");
|
|
3169
|
+
const picked = [];
|
|
3170
|
+
for (const argument of args === null ? [] : children(args)) {
|
|
3171
|
+
if (argument.type === "keyword_argument") {
|
|
3172
|
+
const name = field(argument, "name");
|
|
3173
|
+
if (name !== null) {
|
|
3174
|
+
picked.push(name.text);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
2904
3177
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
rows: pythonRows
|
|
2912
|
-
})
|
|
3178
|
+
return picked;
|
|
3179
|
+
}
|
|
3180
|
+
function selectorOf(chain, valueMethods) {
|
|
3181
|
+
return [
|
|
3182
|
+
...new Set(
|
|
3183
|
+
laterCalls(chain).filter((call) => !valueMethods.includes(methodNameOf(call))).flatMap(keywordNames)
|
|
2913
3184
|
)
|
|
2914
|
-
|
|
3185
|
+
];
|
|
2915
3186
|
}
|
|
2916
|
-
function
|
|
2917
|
-
|
|
3187
|
+
function effectFor(pattern, chain, operation) {
|
|
3188
|
+
const valueMethods = pattern.valueMethods ?? [];
|
|
3189
|
+
const picked = selectorOf(chain, valueMethods);
|
|
3190
|
+
return {
|
|
3191
|
+
type: "interaction",
|
|
3192
|
+
binding: (0, import_ir_core2.storageBinding)({
|
|
3193
|
+
recognition: "python-storage",
|
|
3194
|
+
storageSystem: pattern.storageSystem,
|
|
3195
|
+
scope: pattern.module,
|
|
3196
|
+
container: chain.subject
|
|
3197
|
+
}),
|
|
3198
|
+
callee: chain.last.text,
|
|
3199
|
+
interaction: {
|
|
3200
|
+
class: "storage-access",
|
|
3201
|
+
kind: pattern.writes.includes(operation) ? "write" : "read",
|
|
3202
|
+
fields: fieldsOf(chain, valueMethods),
|
|
3203
|
+
operation,
|
|
3204
|
+
...picked.length > 0 ? { selector: picked } : {}
|
|
3205
|
+
}
|
|
3206
|
+
};
|
|
2918
3207
|
}
|
|
2919
|
-
function
|
|
2920
|
-
|
|
3208
|
+
function fileOf(key) {
|
|
3209
|
+
const at = key.lastIndexOf(":");
|
|
3210
|
+
return at === -1 ? key : key.slice(0, at);
|
|
2921
3211
|
}
|
|
2922
|
-
function
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
return bound;
|
|
3212
|
+
function returnTypeName(node) {
|
|
3213
|
+
if (node === void 0) {
|
|
3214
|
+
return null;
|
|
2926
3215
|
}
|
|
2927
|
-
const
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
local = new import_values4.Evaluator(
|
|
2931
|
-
pythonLowering({
|
|
2932
|
-
context: null,
|
|
2933
|
-
originOf: calleeOrigin,
|
|
2934
|
-
rows: pythonRows
|
|
2935
|
-
})
|
|
2936
|
-
);
|
|
2937
|
-
withoutFacts.set(tree, local);
|
|
3216
|
+
const annotation = field(node, "return_type");
|
|
3217
|
+
if (annotation === null) {
|
|
3218
|
+
return null;
|
|
2938
3219
|
}
|
|
2939
|
-
return
|
|
3220
|
+
return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
|
|
2940
3221
|
}
|
|
2941
|
-
function
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3222
|
+
function storageCallees(calls, filePath, couldMatch) {
|
|
3223
|
+
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)));
|
|
3224
|
+
}
|
|
3225
|
+
function resolvedMethodPattern(options, callee) {
|
|
3226
|
+
const resolved = options.facts.facts("wantedResolves").filter(
|
|
3227
|
+
(row) => String(row[0]) === readKey(options.filePath, callee, enclosingFunction(callee))
|
|
3228
|
+
).map((row) => String(row[1]));
|
|
3229
|
+
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
3230
|
+
if (settled === void 0) {
|
|
3231
|
+
return void 0;
|
|
2947
3232
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
3233
|
+
const typeName = returnTypeName(options.definitionAt(settled));
|
|
3234
|
+
if (typeName === null) {
|
|
3235
|
+
return void 0;
|
|
3236
|
+
}
|
|
3237
|
+
return options.patterns.find(
|
|
3238
|
+
(candidate) => candidate.queryTypes.includes(typeName) && options.facts.facts("pyImport").some(
|
|
3239
|
+
(row) => String(row[0]) === fileOf(settled) && String(row[1]) === candidate.module
|
|
3240
|
+
)
|
|
3241
|
+
);
|
|
2952
3242
|
}
|
|
2953
|
-
function
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
for (const entry of nodes.files) {
|
|
2957
|
-
filesByRoot.set(entry.root.id, entry);
|
|
2958
|
-
rootsByFile.set(entry.file, entry.root);
|
|
3243
|
+
function matchedChains(calls, options) {
|
|
3244
|
+
if (options.patterns.length === 0) {
|
|
3245
|
+
return [];
|
|
2959
3246
|
}
|
|
2960
|
-
const
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
3247
|
+
const named = new Set(
|
|
3248
|
+
options.patterns.flatMap((pattern) => pattern.queryFunctions ?? [])
|
|
3249
|
+
);
|
|
3250
|
+
const chains = chainsIn(calls).filter(
|
|
3251
|
+
(chain) => options.couldMatch.has(methodName(chain.root)) || named.has(methodName(chain.root)) || typedReceiverPattern(options, chain) !== void 0
|
|
3252
|
+
);
|
|
3253
|
+
if (chains.length === 0) {
|
|
3254
|
+
return [];
|
|
3255
|
+
}
|
|
3256
|
+
const alreadyAsked = new Set(
|
|
3257
|
+
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
3258
|
+
);
|
|
3259
|
+
resolveCalls(
|
|
3260
|
+
options.facts,
|
|
3261
|
+
storageCallees(calls, options.filePath, options.couldMatch).filter(
|
|
3262
|
+
(key) => !alreadyAsked.has(key)
|
|
3263
|
+
)
|
|
3264
|
+
);
|
|
3265
|
+
const matched = [];
|
|
3266
|
+
for (const chain of chains) {
|
|
3267
|
+
const callee = field(chain.root, "function");
|
|
3268
|
+
if (callee === null) {
|
|
3269
|
+
continue;
|
|
2964
3270
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
const answer = writtenValueOf(db, key);
|
|
2974
|
-
return answer === null ? null : (0, import_resolution2.nodeOfKey)(rootsByFile, answer);
|
|
2975
|
-
},
|
|
2976
|
-
callable: (call) => {
|
|
2977
|
-
const callee = field(call, "function");
|
|
2978
|
-
const key = callee === null ? null : keyOf2(callee);
|
|
2979
|
-
if (key === null) {
|
|
2980
|
-
return null;
|
|
2981
|
-
}
|
|
2982
|
-
resolveCalls(db, [key]);
|
|
2983
|
-
const resolved = db.facts("wantedResolves").filter((row) => String(row[0]) === key).map((row) => String(row[1]));
|
|
2984
|
-
const settled = resolved.length === 1 ? resolved[0] : void 0;
|
|
2985
|
-
return settled === void 0 ? null : nodes.definitions.get(settled) ?? null;
|
|
3271
|
+
const imported = importedQueryFunction(options, chain);
|
|
3272
|
+
if (imported !== void 0) {
|
|
3273
|
+
matched.push({
|
|
3274
|
+
chain,
|
|
3275
|
+
pattern: imported,
|
|
3276
|
+
operation: methodName(chain.root)
|
|
3277
|
+
});
|
|
3278
|
+
continue;
|
|
2986
3279
|
}
|
|
2987
|
-
|
|
3280
|
+
const typed = typedReceiverPattern(options, chain);
|
|
3281
|
+
if (typed !== void 0) {
|
|
3282
|
+
const silent = (typed.recordsNothing ?? []).includes(
|
|
3283
|
+
methodName(chain.root)
|
|
3284
|
+
);
|
|
3285
|
+
matched.push({
|
|
3286
|
+
chain,
|
|
3287
|
+
pattern: typed,
|
|
3288
|
+
operation: silent ? null : chain.operation
|
|
3289
|
+
});
|
|
3290
|
+
continue;
|
|
3291
|
+
}
|
|
3292
|
+
const pattern = resolvedMethodPattern(options, callee);
|
|
3293
|
+
if (pattern !== void 0) {
|
|
3294
|
+
matched.push({ chain, pattern, operation: chain.operation });
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
return matched;
|
|
3298
|
+
}
|
|
3299
|
+
function storageCallIds(calls, options) {
|
|
3300
|
+
return new Set(
|
|
3301
|
+
matchedChains(calls, options).map(({ chain }) => chain.root.id)
|
|
3302
|
+
);
|
|
3303
|
+
}
|
|
3304
|
+
function storageEffects(calls, options) {
|
|
3305
|
+
return matchedChains(calls, options).flatMap(
|
|
3306
|
+
({ chain, pattern, operation }) => operation === null ? [] : [effectFor(pattern, chain, operation)]
|
|
3307
|
+
);
|
|
2988
3308
|
}
|
|
2989
3309
|
|
|
2990
3310
|
// src/discovery.ts
|
|
@@ -3167,7 +3487,7 @@ function readPathArgument(classification, options) {
|
|
|
3167
3487
|
const first = classification.args[0];
|
|
3168
3488
|
if (first !== void 0) {
|
|
3169
3489
|
const value = evaluatedValue(first.node, options.facts);
|
|
3170
|
-
const path6 = (0,
|
|
3490
|
+
const path6 = (0, import_values7.literalOf)(value) ?? (0, import_values7.pathOf)(value);
|
|
3171
3491
|
if (path6 !== void 0) {
|
|
3172
3492
|
return (0, import_extractor4.writtenReading)(path6, classification.range);
|
|
3173
3493
|
}
|
|
@@ -3249,6 +3569,8 @@ function classRouteUnits(pattern, pack, classification, classNode, module2, opti
|
|
|
3249
3569
|
...entry.mount !== void 0 ? { mount: entry.mount } : {},
|
|
3250
3570
|
requestBodyFromAnnotatedClass: pattern.annotatedClassIsRequestBody === true,
|
|
3251
3571
|
readsReturnedStatus: pattern.statusFromReturnedTuple === true,
|
|
3572
|
+
responseStatusCalls: pattern.responseStatusCalls ?? [],
|
|
3573
|
+
facts: options.facts,
|
|
3252
3574
|
injectedCallees: new Set(pattern.injectedParameterCallees ?? []),
|
|
3253
3575
|
definitionNode: maybeMethod,
|
|
3254
3576
|
enclosingScope: classScope,
|
|
@@ -3433,36 +3755,106 @@ function effectsReaching(effects, conditions) {
|
|
|
3433
3755
|
(effect) => (0, import_extractor4.guardsHoldOn)(effect.preconditions, conditions)
|
|
3434
3756
|
);
|
|
3435
3757
|
}
|
|
3436
|
-
function
|
|
3437
|
-
if (!readsReturnedStatus) {
|
|
3438
|
-
return
|
|
3758
|
+
function returnStatusReading(statement, ctx) {
|
|
3759
|
+
if (!ctx.readsReturnedStatus) {
|
|
3760
|
+
return ctx.declaredStatus.reading;
|
|
3439
3761
|
}
|
|
3440
|
-
const
|
|
3441
|
-
const
|
|
3442
|
-
if (
|
|
3762
|
+
const read = statusOfReturn(statement);
|
|
3763
|
+
const range = rangeOf(statement);
|
|
3764
|
+
if (read.kind === "status") {
|
|
3765
|
+
return (0, import_extractor4.writtenReading)(read.value, range);
|
|
3766
|
+
}
|
|
3767
|
+
if (read.kind === "none") {
|
|
3768
|
+
return import_extractor4.absentReading;
|
|
3769
|
+
}
|
|
3770
|
+
return (0, import_extractor4.unreadableReading)(
|
|
3771
|
+
"This return gives a status this reading cannot resolve to a number, so this outcome claims none",
|
|
3772
|
+
range
|
|
3773
|
+
);
|
|
3774
|
+
}
|
|
3775
|
+
function returnBranch(statement, ctx) {
|
|
3776
|
+
const range = rangeOf(statement);
|
|
3777
|
+
const writtenBody = returnedBodyShape(statement);
|
|
3778
|
+
return {
|
|
3779
|
+
terminal: {
|
|
3780
|
+
kind: "response",
|
|
3781
|
+
// These stay null here. The extractor fills them in from the two
|
|
3782
|
+
// readings below.
|
|
3783
|
+
statusCode: null,
|
|
3784
|
+
body: { typeText: null, shape: null },
|
|
3785
|
+
exceptionType: null,
|
|
3786
|
+
message: null,
|
|
3787
|
+
component: null,
|
|
3788
|
+
renderTree: null,
|
|
3789
|
+
delegateTarget: null,
|
|
3790
|
+
emitEvent: null,
|
|
3791
|
+
location: range
|
|
3792
|
+
},
|
|
3793
|
+
statusCodeReading: {
|
|
3794
|
+
reading: returnStatusReading(statement, ctx),
|
|
3795
|
+
...ctx.declaredStatus.libraryDefault !== void 0 ? { libraryDefault: ctx.declaredStatus.libraryDefault } : {}
|
|
3796
|
+
},
|
|
3797
|
+
bodyShapeReading: {
|
|
3798
|
+
reading: writtenBody === null ? ctx.responseShape.reading : (0, import_extractor4.writtenReading)(writtenBody, range)
|
|
3799
|
+
},
|
|
3800
|
+
location: range
|
|
3801
|
+
};
|
|
3802
|
+
}
|
|
3803
|
+
function terminalBranchOf(found, ctx) {
|
|
3804
|
+
const table = {
|
|
3805
|
+
return: (variant) => returnBranch(variant.statement, ctx),
|
|
3806
|
+
raise: (variant) => ({
|
|
3807
|
+
terminal: variant.terminal,
|
|
3808
|
+
location: rangeOf(variant.statement)
|
|
3809
|
+
})
|
|
3810
|
+
};
|
|
3811
|
+
return (0, import_behavioral_ir2.dispatchByType)(table, found);
|
|
3812
|
+
}
|
|
3813
|
+
function routeTerminals(body, raised) {
|
|
3814
|
+
const found = [
|
|
3815
|
+
...returnStatements(body).map(
|
|
3816
|
+
(statement) => ({ type: "return", statement })
|
|
3817
|
+
),
|
|
3818
|
+
...raised.map(
|
|
3819
|
+
(response) => ({
|
|
3820
|
+
type: "raise",
|
|
3821
|
+
statement: response.statement,
|
|
3822
|
+
terminal: response.terminal
|
|
3823
|
+
})
|
|
3824
|
+
)
|
|
3825
|
+
];
|
|
3826
|
+
return found.sort((a, b) => a.statement.startIndex - b.statement.startIndex);
|
|
3827
|
+
}
|
|
3828
|
+
function branchesPerTerminal(options) {
|
|
3829
|
+
const body = field(options.definitionNode, "body");
|
|
3830
|
+
const raised = raisedResponses(body, {
|
|
3831
|
+
calls: options.responseStatusCalls,
|
|
3832
|
+
module: options.module,
|
|
3833
|
+
facts: options.facts
|
|
3834
|
+
});
|
|
3835
|
+
const terminals = routeTerminals(body, raised);
|
|
3836
|
+
if (terminals.length < 2) {
|
|
3443
3837
|
return null;
|
|
3444
3838
|
}
|
|
3445
|
-
const
|
|
3839
|
+
const statements = terminals.map((found) => found.statement);
|
|
3840
|
+
const thrownByCall = new NodeSet(
|
|
3841
|
+
raised.filter((response) => response.thrownByCall).map((response) => response.statement)
|
|
3842
|
+
);
|
|
3843
|
+
const lowered = lowerPythonBody(body, statements, thrownByCall);
|
|
3446
3844
|
const enumerated = (0, import_extractor4.enumerateOrDegrade)(
|
|
3447
3845
|
{
|
|
3448
3846
|
statements: lowered.statements,
|
|
3449
3847
|
terminalsByStmt: lowered.terminalsByStmt
|
|
3450
3848
|
},
|
|
3451
|
-
|
|
3849
|
+
statements
|
|
3452
3850
|
);
|
|
3453
3851
|
const branches = [];
|
|
3454
|
-
for (const
|
|
3455
|
-
const paths = enumerated.byTerminal.get(statement);
|
|
3852
|
+
for (const found of terminals) {
|
|
3853
|
+
const paths = enumerated.byTerminal.get(found.statement);
|
|
3456
3854
|
if (paths === void 0) {
|
|
3457
3855
|
continue;
|
|
3458
3856
|
}
|
|
3459
|
-
const
|
|
3460
|
-
const range = rangeOf(statement);
|
|
3461
|
-
const writtenBody = returnedBodyShape(statement);
|
|
3462
|
-
const statusReading = read.kind === "status" ? (0, import_extractor4.writtenReading)(read.value, range) : read.kind === "none" ? import_extractor4.absentReading : (0, import_extractor4.unreadableReading)(
|
|
3463
|
-
"This return gives a status this reading cannot resolve to a number, so this outcome claims none",
|
|
3464
|
-
range
|
|
3465
|
-
);
|
|
3857
|
+
const branch = terminalBranchOf(found, options.ctx);
|
|
3466
3858
|
for (const path6 of paths) {
|
|
3467
3859
|
const conditions = path6.map((condition) => ({
|
|
3468
3860
|
sourceText: condition.sourceText,
|
|
@@ -3471,28 +3863,9 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
|
|
|
3471
3863
|
source: condition.source
|
|
3472
3864
|
}));
|
|
3473
3865
|
branches.push({
|
|
3866
|
+
...branch,
|
|
3474
3867
|
conditions,
|
|
3475
|
-
|
|
3476
|
-
kind: "response",
|
|
3477
|
-
statusCode: null,
|
|
3478
|
-
body: { typeText: null, shape: null },
|
|
3479
|
-
exceptionType: null,
|
|
3480
|
-
message: null,
|
|
3481
|
-
component: null,
|
|
3482
|
-
renderTree: null,
|
|
3483
|
-
delegateTarget: null,
|
|
3484
|
-
emitEvent: null,
|
|
3485
|
-
location: range
|
|
3486
|
-
},
|
|
3487
|
-
statusCodeReading: {
|
|
3488
|
-
reading: statusReading,
|
|
3489
|
-
...declaredStatus.libraryDefault !== void 0 ? { libraryDefault: declaredStatus.libraryDefault } : {}
|
|
3490
|
-
},
|
|
3491
|
-
bodyShapeReading: {
|
|
3492
|
-
reading: writtenBody === null ? responseShape.reading : (0, import_extractor4.writtenReading)(writtenBody, range)
|
|
3493
|
-
},
|
|
3494
|
-
effects: effectsReaching(effects, conditions),
|
|
3495
|
-
location: range,
|
|
3868
|
+
effects: effectsReaching(options.effects, conditions),
|
|
3496
3869
|
isDefault: path6.length === 0
|
|
3497
3870
|
});
|
|
3498
3871
|
}
|
|
@@ -3597,6 +3970,8 @@ function functionRouteUnits(pattern, pack, verb, classification, functionNode, m
|
|
|
3597
3970
|
...entry.mount !== void 0 ? { mount: entry.mount } : {},
|
|
3598
3971
|
requestBodyFromAnnotatedClass: pattern.annotatedClassIsRequestBody === true,
|
|
3599
3972
|
readsReturnedStatus: pattern.statusFromReturnedTuple === true,
|
|
3973
|
+
responseStatusCalls: pattern.responseStatusCalls ?? [],
|
|
3974
|
+
facts: options.facts,
|
|
3600
3975
|
injectedCallees: new Set(pattern.injectedParameterCallees ?? []),
|
|
3601
3976
|
definitionNode: functionNode,
|
|
3602
3977
|
enclosingScope: module2.moduleScope,
|
|
@@ -3705,17 +4080,18 @@ function buildRouteUnit(options) {
|
|
|
3705
4080
|
);
|
|
3706
4081
|
const effects = invocationEffects(definitionNode);
|
|
3707
4082
|
const extra = recognizedBodyEffects(definitionNode, module2, storageLookup);
|
|
3708
|
-
const
|
|
3709
|
-
readsReturnedStatus,
|
|
4083
|
+
const perTerminal = branchesPerTerminal({
|
|
3710
4084
|
definitionNode,
|
|
3711
4085
|
effects,
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
4086
|
+
module: module2,
|
|
4087
|
+
facts: options.facts,
|
|
4088
|
+
responseStatusCalls: options.responseStatusCalls,
|
|
4089
|
+
ctx: { readsReturnedStatus, responseShape, declaredStatus: statusCode }
|
|
4090
|
+
});
|
|
4091
|
+
const branches = (perTerminal ?? []).map(
|
|
3716
4092
|
(branch) => extra.length === 0 ? branch : { ...branch, extraEffects: extra }
|
|
3717
4093
|
);
|
|
3718
|
-
if (
|
|
4094
|
+
if (perTerminal === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" || extra.length > 0 || effects.length > 0)) {
|
|
3719
4095
|
branches.push({
|
|
3720
4096
|
conditions: [],
|
|
3721
4097
|
terminal: {
|
|
@@ -3797,6 +4173,15 @@ function recognizedBodyEffects(definitionNode, module2, storageLookup) {
|
|
|
3797
4173
|
];
|
|
3798
4174
|
return [...envReadEffects(definitionNode, module2), ...storage];
|
|
3799
4175
|
}
|
|
4176
|
+
function recognizedCallIds(definitionNode, storageLookup) {
|
|
4177
|
+
if (storageLookup === void 0) {
|
|
4178
|
+
return /* @__PURE__ */ new Set();
|
|
4179
|
+
}
|
|
4180
|
+
return storageCallIds(bodyCalls(definitionNode), {
|
|
4181
|
+
...storageLookup,
|
|
4182
|
+
filePath: storageLookup.factsPath
|
|
4183
|
+
});
|
|
4184
|
+
}
|
|
3800
4185
|
function bodyContentOf(bodyNode) {
|
|
3801
4186
|
for (const stmt of bodyStatements(bodyNode)) {
|
|
3802
4187
|
if (stmt.type === "pass_statement") {
|
|
@@ -3911,26 +4296,6 @@ function isInjectedParameter(param, injectedCallees) {
|
|
|
3911
4296
|
};
|
|
3912
4297
|
return containsInjectorCall(field(param, "type"), 0);
|
|
3913
4298
|
}
|
|
3914
|
-
function parameterNameAndType(param) {
|
|
3915
|
-
if (param.type === "identifier") {
|
|
3916
|
-
return { name: param.text, typeNode: null };
|
|
3917
|
-
}
|
|
3918
|
-
if (param.type === "typed_parameter") {
|
|
3919
|
-
const inner = param.namedChildren.find(
|
|
3920
|
-
(child) => child !== null && child.type === "identifier"
|
|
3921
|
-
);
|
|
3922
|
-
return inner !== void 0 ? { name: inner.text, typeNode: field(param, "type") } : null;
|
|
3923
|
-
}
|
|
3924
|
-
if (param.type === "default_parameter") {
|
|
3925
|
-
const nameNode = field(param, "name");
|
|
3926
|
-
return nameNode?.type === "identifier" ? { name: nameNode.text, typeNode: null } : null;
|
|
3927
|
-
}
|
|
3928
|
-
if (param.type === "typed_default_parameter") {
|
|
3929
|
-
const nameNode = field(param, "name");
|
|
3930
|
-
return nameNode !== null ? { name: nameNode.text, typeNode: field(param, "type") } : null;
|
|
3931
|
-
}
|
|
3932
|
-
return null;
|
|
3933
|
-
}
|
|
3934
4299
|
|
|
3935
4300
|
// src/facts/why.ts
|
|
3936
4301
|
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
@@ -4441,7 +4806,11 @@ function reachedFunctions(seeds, options) {
|
|
|
4441
4806
|
if (source === void 0) {
|
|
4442
4807
|
continue;
|
|
4443
4808
|
}
|
|
4444
|
-
const scan = scanBody(
|
|
4809
|
+
const scan = scanBody(
|
|
4810
|
+
source,
|
|
4811
|
+
ctx,
|
|
4812
|
+
recognizedCallIds(source.node, options.storageFor(source.file))
|
|
4813
|
+
);
|
|
4445
4814
|
if (scan.stops.length > 0) {
|
|
4446
4815
|
stopsByKey.set(key, scan.stops);
|
|
4447
4816
|
}
|
|
@@ -4506,7 +4875,7 @@ var EMPTY_SCAN = {
|
|
|
4506
4875
|
parameterCalls: [],
|
|
4507
4876
|
passedPositions: /* @__PURE__ */ new Set()
|
|
4508
4877
|
};
|
|
4509
|
-
function scanBody(source, ctx) {
|
|
4878
|
+
function scanBody(source, ctx, recognized) {
|
|
4510
4879
|
const followed = [];
|
|
4511
4880
|
const stops = [];
|
|
4512
4881
|
const placements = new import_behavioral_ir3.TargetPlacements();
|
|
@@ -4571,7 +4940,7 @@ function scanBody(source, ctx) {
|
|
|
4571
4940
|
);
|
|
4572
4941
|
if (outcome.kind === "stopped") {
|
|
4573
4942
|
const stopKey = `${outcome.reason}:${callee}`;
|
|
4574
|
-
if (!seen.has(stopKey) && (0, import_behavioral_ir3.worthRecording)(outcome.reason)) {
|
|
4943
|
+
if (!seen.has(stopKey) && !recognized.has(call.id) && (0, import_behavioral_ir3.worthRecording)(outcome.reason)) {
|
|
4575
4944
|
seen.add(stopKey);
|
|
4576
4945
|
stops.push({ callee, reason: outcome.reason });
|
|
4577
4946
|
}
|