diffninja 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -0
  3. package/dist/calltree.d.ts +47 -0
  4. package/dist/calltree.js +296 -0
  5. package/dist/cli.d.ts +57 -0
  6. package/dist/cli.js +340 -0
  7. package/dist/diff.d.ts +7 -0
  8. package/dist/diff.js +114 -0
  9. package/dist/extract.d.ts +26 -0
  10. package/dist/extract.js +152 -0
  11. package/dist/git.d.ts +40 -0
  12. package/dist/git.js +288 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.js +8 -0
  15. package/dist/infer.d.ts +21 -0
  16. package/dist/infer.js +189 -0
  17. package/dist/languages/bash.d.ts +2 -0
  18. package/dist/languages/bash.js +208 -0
  19. package/dist/languages/c.d.ts +2 -0
  20. package/dist/languages/c.js +218 -0
  21. package/dist/languages/call-syntax.d.ts +125 -0
  22. package/dist/languages/call-syntax.js +997 -0
  23. package/dist/languages/cpp.d.ts +2 -0
  24. package/dist/languages/cpp.js +321 -0
  25. package/dist/languages/csharp.d.ts +2 -0
  26. package/dist/languages/csharp.js +324 -0
  27. package/dist/languages/elixir.d.ts +2 -0
  28. package/dist/languages/elixir.js +331 -0
  29. package/dist/languages/go.d.ts +2 -0
  30. package/dist/languages/go.js +299 -0
  31. package/dist/languages/grammars.d.ts +50 -0
  32. package/dist/languages/grammars.js +351 -0
  33. package/dist/languages/haskell.d.ts +2 -0
  34. package/dist/languages/haskell.js +250 -0
  35. package/dist/languages/java.d.ts +2 -0
  36. package/dist/languages/java.js +351 -0
  37. package/dist/languages/javascript.d.ts +4 -0
  38. package/dist/languages/javascript.js +648 -0
  39. package/dist/languages/kotlin.d.ts +2 -0
  40. package/dist/languages/kotlin.js +368 -0
  41. package/dist/languages/lua.d.ts +2 -0
  42. package/dist/languages/lua.js +212 -0
  43. package/dist/languages/ocaml.d.ts +2 -0
  44. package/dist/languages/ocaml.js +291 -0
  45. package/dist/languages/perl.d.ts +2 -0
  46. package/dist/languages/perl.js +418 -0
  47. package/dist/languages/php.d.ts +2 -0
  48. package/dist/languages/php.js +397 -0
  49. package/dist/languages/python.d.ts +2 -0
  50. package/dist/languages/python.js +376 -0
  51. package/dist/languages/registry.d.ts +7 -0
  52. package/dist/languages/registry.js +69 -0
  53. package/dist/languages/ruby.d.ts +2 -0
  54. package/dist/languages/ruby.js +391 -0
  55. package/dist/languages/rust.d.ts +2 -0
  56. package/dist/languages/rust.js +261 -0
  57. package/dist/languages/scala.d.ts +2 -0
  58. package/dist/languages/scala.js +307 -0
  59. package/dist/languages/solidity.d.ts +2 -0
  60. package/dist/languages/solidity.js +240 -0
  61. package/dist/languages/swift.d.ts +2 -0
  62. package/dist/languages/swift.js +268 -0
  63. package/dist/languages/types.d.ts +36 -0
  64. package/dist/languages/types.js +74 -0
  65. package/dist/languages/typescript-contracts.d.ts +57 -0
  66. package/dist/languages/typescript-contracts.js +528 -0
  67. package/dist/languages/typescript-dispatch.d.ts +68 -0
  68. package/dist/languages/typescript-dispatch.js +710 -0
  69. package/dist/languages/typescript.d.ts +4 -0
  70. package/dist/languages/typescript.js +722 -0
  71. package/dist/languages/zig.d.ts +2 -0
  72. package/dist/languages/zig.js +243 -0
  73. package/dist/loc.d.ts +17 -0
  74. package/dist/loc.js +34 -0
  75. package/dist/reach.d.ts +17 -0
  76. package/dist/reach.js +65 -0
  77. package/dist/render.d.ts +18 -0
  78. package/dist/render.js +83 -0
  79. package/dist/review/brand.d.ts +8 -0
  80. package/dist/review/brand.js +25 -0
  81. package/dist/review/call-context.d.ts +27 -0
  82. package/dist/review/call-context.js +446 -0
  83. package/dist/review/call-flow-html.d.ts +32 -0
  84. package/dist/review/call-flow-html.js +1870 -0
  85. package/dist/review/call-flow-nav.d.ts +151 -0
  86. package/dist/review/call-flow-nav.js +317 -0
  87. package/dist/review/call-flow.d.ts +47 -0
  88. package/dist/review/call-flow.js +229 -0
  89. package/dist/review/change-facts.d.ts +69 -0
  90. package/dist/review/change-facts.js +729 -0
  91. package/dist/review/cli.d.ts +2 -0
  92. package/dist/review/cli.js +50 -0
  93. package/dist/review/connected-analysis.d.ts +100 -0
  94. package/dist/review/connected-analysis.js +163 -0
  95. package/dist/review/connected-html.d.ts +17 -0
  96. package/dist/review/connected-html.js +2853 -0
  97. package/dist/review/connected.d.ts +23 -0
  98. package/dist/review/connected.js +141 -0
  99. package/dist/review/escape-html.d.ts +2 -0
  100. package/dist/review/escape-html.js +9 -0
  101. package/dist/review/evidence-html.d.ts +21 -0
  102. package/dist/review/evidence-html.js +521 -0
  103. package/dist/review/evidence-syntax.d.ts +132 -0
  104. package/dist/review/evidence-syntax.js +478 -0
  105. package/dist/review/evidence-types.d.ts +62 -0
  106. package/dist/review/evidence-types.js +1 -0
  107. package/dist/review/evidence.d.ts +31 -0
  108. package/dist/review/evidence.js +1603 -0
  109. package/dist/review/file-role.d.ts +9 -0
  110. package/dist/review/file-role.js +29 -0
  111. package/dist/review/github.d.ts +204 -0
  112. package/dist/review/github.js +1245 -0
  113. package/dist/review/history.d.ts +101 -0
  114. package/dist/review/history.js +412 -0
  115. package/dist/review/html.d.ts +34 -0
  116. package/dist/review/html.js +1104 -0
  117. package/dist/review/input.d.ts +10 -0
  118. package/dist/review/input.js +113 -0
  119. package/dist/review/intent.d.ts +4 -0
  120. package/dist/review/intent.js +75 -0
  121. package/dist/review/mcp-cli.d.ts +2 -0
  122. package/dist/review/mcp-cli.js +25 -0
  123. package/dist/review/mcp.d.ts +12 -0
  124. package/dist/review/mcp.js +414 -0
  125. package/dist/review/module-resolution.d.ts +2 -0
  126. package/dist/review/module-resolution.js +86 -0
  127. package/dist/review/palette.d.ts +7 -0
  128. package/dist/review/palette.js +104 -0
  129. package/dist/review/pipeline.d.ts +77 -0
  130. package/dist/review/pipeline.js +227 -0
  131. package/dist/review/pr-input.d.ts +19 -0
  132. package/dist/review/pr-input.js +130 -0
  133. package/dist/review/questions.d.ts +201 -0
  134. package/dist/review/questions.js +174 -0
  135. package/dist/review/reference-check.d.ts +7 -0
  136. package/dist/review/reference-check.js +733 -0
  137. package/dist/review/report-pages.d.ts +109 -0
  138. package/dist/review/report-pages.js +328 -0
  139. package/dist/review/service.d.ts +23 -0
  140. package/dist/review/service.js +198 -0
  141. package/dist/review/setup.d.ts +112 -0
  142. package/dist/review/setup.js +549 -0
  143. package/dist/review/source.d.ts +26 -0
  144. package/dist/review/source.js +276 -0
  145. package/dist/review/toml.d.ts +38 -0
  146. package/dist/review/toml.js +565 -0
  147. package/dist/review/types.d.ts +179 -0
  148. package/dist/review/types.js +1 -0
  149. package/dist/run.d.ts +49 -0
  150. package/dist/run.js +311 -0
  151. package/dist/types.d.ts +366 -0
  152. package/dist/types.js +83 -0
  153. package/package.json +88 -0
  154. package/scripts/ensure-native-grammar.mjs +188 -0
@@ -0,0 +1,208 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, collapseWs, locFromNode, namedChildren, sameNode, } from "./types.js";
3
+ const SKIP_COMMANDS = new Set([
4
+ ":",
5
+ "true",
6
+ "false",
7
+ "echo",
8
+ "printf",
9
+ "local",
10
+ "export",
11
+ "readonly",
12
+ "return",
13
+ "exit",
14
+ "shift",
15
+ "set",
16
+ "unset",
17
+ "cd",
18
+ "pwd",
19
+ "test",
20
+ "[",
21
+ "[[",
22
+ "source",
23
+ ".",
24
+ "eval",
25
+ "exec",
26
+ "command",
27
+ "builtin",
28
+ "type",
29
+ "hash",
30
+ "wait",
31
+ "trap",
32
+ "read",
33
+ "mapfile",
34
+ "declare",
35
+ "typeset",
36
+ ]);
37
+ function commandName(cmd) {
38
+ const nameNode = childByType(cmd, "command_name") ?? namedChildren(cmd)[0] ?? null;
39
+ if (!nameNode)
40
+ return null;
41
+ const word = nameNode.type === "word"
42
+ ? nameNode
43
+ : childByType(nameNode, "word") ?? nameNode.namedChild(0);
44
+ const text = (word ?? nameNode).text;
45
+ if (!text || SKIP_COMMANDS.has(text))
46
+ return null;
47
+ // Dynamic: "$cmd" / "${cmd}" / $(...) as name
48
+ if (text.startsWith("$") || nameNode.type === "command_substitution") {
49
+ return null;
50
+ }
51
+ return text;
52
+ }
53
+ function collectStatements(file, statements) {
54
+ const steps = [];
55
+ const seen = new Set();
56
+ const addCall = (key, node) => {
57
+ const mark = `${key}:${node.startIndex}`;
58
+ if (seen.has(mark))
59
+ return;
60
+ seen.add(mark);
61
+ const step = { type: "call", key, ...locFromNode(file, node) };
62
+ step.syntax = heuristicCallSyntax(node, key);
63
+ steps.push(step);
64
+ };
65
+ const walk = (node) => {
66
+ // Nested function bodies are not attributed to the outer function
67
+ if (node.type === "function_definition")
68
+ return;
69
+ if (node.type === "if_statement") {
70
+ const kids = namedChildren(node);
71
+ const cond = kids.find((c) => c.type === "test_command" ||
72
+ c.type === "condition" ||
73
+ c.type === "command" ||
74
+ c.type === "list" ||
75
+ c.type === "parenthesized_expression" ||
76
+ c.type === "unary_expression" ||
77
+ c.type === "binary_expression" ||
78
+ c.type === "negated_command" ||
79
+ c.type === "subscript" ||
80
+ c.type === "variable_name" ||
81
+ c.type === "string" ||
82
+ c.type === "word") ?? kids[0] ?? null;
83
+ // Prefer test_command text
84
+ const test = childByType(node, "test_command") ?? cond;
85
+ const condText = test ? collapseWs(test.text) : "";
86
+ // Consequent: statements before else/elif clauses
87
+ const elseClause = childByType(node, "else_clause");
88
+ const elifClauses = namedChildren(node).filter((c) => c.type === "elif_clause");
89
+ const consequent = kids.filter((c) => !sameNode(c, test) &&
90
+ c.type !== "else_clause" &&
91
+ c.type !== "elif_clause" &&
92
+ c.type !== "test_command");
93
+ // If first kid is the condition already captured as test, drop it from consequent
94
+ const thenStmts = consequent.filter((c) => !sameNode(c, cond));
95
+ steps.push({
96
+ type: "branch",
97
+ key: condText ? `if:${condText}` : "if",
98
+ label: condText ? `if ${condText}` : "if",
99
+ ...locFromNode(file, test ?? node),
100
+ children: collectStatements(file, thenStmts),
101
+ });
102
+ for (const clause of elifClauses) {
103
+ const elifKids = namedChildren(clause);
104
+ const elifTest = childByType(clause, "test_command") ?? elifKids[0] ?? null;
105
+ const text = elifTest ? collapseWs(elifTest.text) : "";
106
+ const body = elifKids.filter((c) => !sameNode(c, elifTest));
107
+ steps.push({
108
+ type: "branch",
109
+ key: text ? `else-if:${text}` : "else-if",
110
+ label: text ? `elif ${text}` : "elif",
111
+ ...locFromNode(file, elifTest ?? clause),
112
+ children: collectStatements(file, body),
113
+ });
114
+ }
115
+ if (elseClause) {
116
+ steps.push({
117
+ type: "branch",
118
+ key: "else",
119
+ label: "else",
120
+ ...locFromNode(file, elseClause),
121
+ children: collectStatements(file, namedChildren(elseClause)),
122
+ });
123
+ }
124
+ return;
125
+ }
126
+ if (node.type === "case_statement") {
127
+ for (const item of namedChildren(node)) {
128
+ if (item.type !== "case_item")
129
+ continue;
130
+ const kids = namedChildren(item);
131
+ const pattern = kids.find((c) => c.type === "word" ||
132
+ c.type === "string" ||
133
+ c.type === "raw_string" ||
134
+ c.type === "extglob_pattern" ||
135
+ c.type === "concatenation" ||
136
+ c.type === "ansi_c_string") ?? kids[0] ?? null;
137
+ const text = pattern ? collapseWs(pattern.text) : "";
138
+ const body = kids.filter((c) => !sameNode(c, pattern));
139
+ steps.push({
140
+ type: "branch",
141
+ key: text ? `case:${text}` : "case",
142
+ label: text ? `case ${text}` : "case",
143
+ ...locFromNode(file, pattern ?? item),
144
+ children: collectStatements(file, body),
145
+ });
146
+ }
147
+ return;
148
+ }
149
+ if (node.type === "command") {
150
+ const name = commandName(node);
151
+ if (name)
152
+ addCall(name, node);
153
+ // Walk args / substitutions (including when the name itself is $(...))
154
+ for (const child of namedChildren(node)) {
155
+ if (name && child.type === "command_name")
156
+ continue;
157
+ walk(child);
158
+ }
159
+ return;
160
+ }
161
+ if (node.type === "command_substitution") {
162
+ for (const child of namedChildren(node))
163
+ walk(child);
164
+ return;
165
+ }
166
+ for (const child of namedChildren(node))
167
+ walk(child);
168
+ };
169
+ for (const stmt of statements)
170
+ walk(stmt);
171
+ return steps;
172
+ }
173
+ function handleFunction(file, node, functions) {
174
+ const name = childByType(node, "word")?.text ??
175
+ namedChildren(node).find((c) => c.type === "word")?.text ??
176
+ null;
177
+ if (!name)
178
+ return;
179
+ const body = childByType(node, "compound_statement") ??
180
+ childByType(node, "subshell") ??
181
+ childByType(node, "test_command") ??
182
+ null;
183
+ const stmts = body ? namedChildren(body) : [];
184
+ functions.push({
185
+ key: name,
186
+ label: `${name}()`,
187
+ file,
188
+ steps: collectStatements(file, stmts),
189
+ exported: true,
190
+ start: node.startIndex,
191
+ end: node.endIndex,
192
+ });
193
+ }
194
+ function extractFromTree(file, _source, tree) {
195
+ const functions = [];
196
+ for (const stmt of namedChildren(tree.rootNode)) {
197
+ if (stmt.type === "function_definition") {
198
+ handleFunction(file, stmt, functions);
199
+ }
200
+ }
201
+ return functions;
202
+ }
203
+ export const bashExtractor = {
204
+ id: "bash",
205
+ extensions: [".sh", ".bash"],
206
+ grammarPackage: "tree-sitter-bash",
207
+ extract: extractFromTree,
208
+ };
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const cExtractor: LanguageExtractor;
@@ -0,0 +1,218 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, declaredParamsOf, collapseWs, locFromNode, namedChildren, sameNode, } from "./types.js";
3
+ function hasStatic(node) {
4
+ return namedChildren(node).some((c) => c.type === "storage_class_specifier" && c.text === "static");
5
+ }
6
+ function unwrapDeclarator(node) {
7
+ let cur = node;
8
+ while (cur) {
9
+ if (cur.type === "function_declarator")
10
+ return cur;
11
+ const next = childByType(cur, "function_declarator") ??
12
+ childByType(cur, "pointer_declarator") ??
13
+ childByType(cur, "parenthesized_declarator") ??
14
+ childByType(cur, "array_declarator") ??
15
+ null;
16
+ if (!next || sameNode(next, cur))
17
+ break;
18
+ cur = next;
19
+ }
20
+ return cur?.type === "function_declarator" ? cur : null;
21
+ }
22
+ function functionName(def) {
23
+ const declarator = unwrapDeclarator(namedChildren(def).find((c) => c.type === "function_declarator" ||
24
+ c.type === "pointer_declarator" ||
25
+ c.type === "parenthesized_declarator") ?? null) ?? childByType(def, "function_declarator");
26
+ if (!declarator)
27
+ return null;
28
+ return (childByType(declarator, "identifier")?.text ??
29
+ childByType(declarator, "field_identifier")?.text ??
30
+ null);
31
+ }
32
+ function getParamsLabel(def) {
33
+ const declarator = unwrapDeclarator(namedChildren(def).find((c) => c.type === "function_declarator" ||
34
+ c.type === "pointer_declarator" ||
35
+ c.type === "parenthesized_declarator") ?? null) ?? childByType(def, "function_declarator");
36
+ const params = declarator ? childByType(declarator, "parameter_list") : null;
37
+ if (!params)
38
+ return "()";
39
+ const names = [];
40
+ for (const p of namedChildren(params)) {
41
+ if (p.type !== "parameter_declaration")
42
+ continue;
43
+ if (p.text === "void")
44
+ continue;
45
+ const id = childByType(p, "identifier") ??
46
+ childByType(childByType(p, "pointer_declarator") ?? p, "identifier");
47
+ names.push(id?.text ?? "_");
48
+ }
49
+ return names.length === 0 ? "()" : `(${names.join(", ")})`;
50
+ }
51
+ function calleeKey(node) {
52
+ if (node.type === "identifier")
53
+ return node.text;
54
+ if (node.type === "field_expression") {
55
+ const object = node.namedChild(0);
56
+ const field = childByType(node, "field_identifier");
57
+ if (!object || !field)
58
+ return null;
59
+ if (object.type === "identifier") {
60
+ return `${object.text}.${field.text}`;
61
+ }
62
+ return field.text;
63
+ }
64
+ // Ignore computed / weird callees
65
+ return null;
66
+ }
67
+ function condText(node) {
68
+ if (!node)
69
+ return "";
70
+ if (node.type === "parenthesized_expression") {
71
+ const inner = node.namedChild(0);
72
+ if (inner)
73
+ return collapseWs(inner.text);
74
+ }
75
+ return collapseWs(node.text);
76
+ }
77
+ function collectStatements(file, statements) {
78
+ const steps = [];
79
+ const seen = new Set();
80
+ const addCall = (key, node) => {
81
+ const mark = `${key}:${node.startIndex}`;
82
+ if (seen.has(mark))
83
+ return;
84
+ seen.add(mark);
85
+ const step = { type: "call", key, ...locFromNode(file, node) };
86
+ step.syntax = heuristicCallSyntax(node, key);
87
+ steps.push(step);
88
+ };
89
+ const walk = (node) => {
90
+ // Nested function definitions (GCC extension) — skip bodies
91
+ if (node.type === "function_definition")
92
+ return;
93
+ if (node.type === "if_statement") {
94
+ const cond = node.childForFieldName("condition") ??
95
+ childByType(node, "parenthesized_expression");
96
+ const consequent = node.childForFieldName("consequence") ??
97
+ namedChildren(node).find((c) => c.type !== "parenthesized_expression" && c.type !== "else_clause") ??
98
+ null;
99
+ const text = condText(cond);
100
+ steps.push({
101
+ type: "branch",
102
+ key: text ? `if:${text}` : "if",
103
+ label: text ? `if ${text}` : "if",
104
+ ...locFromNode(file, cond ?? node),
105
+ children: consequent ? collectStatements(file, [consequent]) : [],
106
+ });
107
+ let elseClause = childByType(node, "else_clause");
108
+ while (elseClause) {
109
+ const inner = elseClause.namedChild(0);
110
+ if (!inner)
111
+ break;
112
+ if (inner.type === "if_statement") {
113
+ const elifCond = inner.childForFieldName("condition") ??
114
+ childByType(inner, "parenthesized_expression");
115
+ const elifText = condText(elifCond);
116
+ const elifCons = inner.childForFieldName("consequence") ??
117
+ namedChildren(inner).find((c) => c.type !== "parenthesized_expression" &&
118
+ c.type !== "else_clause") ??
119
+ null;
120
+ steps.push({
121
+ type: "branch",
122
+ key: elifText ? `else-if:${elifText}` : "else-if",
123
+ label: elifText ? `else if ${elifText}` : "else if",
124
+ ...locFromNode(file, elifCond ?? elseClause),
125
+ children: elifCons ? collectStatements(file, [elifCons]) : [],
126
+ });
127
+ elseClause = childByType(inner, "else_clause");
128
+ continue;
129
+ }
130
+ steps.push({
131
+ type: "branch",
132
+ key: "else",
133
+ label: "else",
134
+ ...locFromNode(file, elseClause),
135
+ children: collectStatements(file, [inner]),
136
+ });
137
+ break;
138
+ }
139
+ return;
140
+ }
141
+ if (node.type === "switch_statement") {
142
+ const body = node.childForFieldName("body") ??
143
+ childByType(node, "compound_statement");
144
+ if (body) {
145
+ for (const clause of namedChildren(body)) {
146
+ if (clause.type !== "case_statement")
147
+ continue;
148
+ const value = clause.childForFieldName("value");
149
+ const kids = namedChildren(clause).filter((c) => !sameNode(c, value) && c.type !== "break_statement");
150
+ if (value) {
151
+ const text = collapseWs(value.text);
152
+ steps.push({
153
+ type: "branch",
154
+ key: `case:${text}`,
155
+ label: `case ${text}`,
156
+ ...locFromNode(file, value),
157
+ children: collectStatements(file, kids),
158
+ });
159
+ }
160
+ else {
161
+ steps.push({
162
+ type: "branch",
163
+ key: "default",
164
+ label: "default",
165
+ ...locFromNode(file, clause),
166
+ children: collectStatements(file, kids),
167
+ });
168
+ }
169
+ }
170
+ }
171
+ return;
172
+ }
173
+ if (node.type === "call_expression") {
174
+ const callee = node.namedChild(0);
175
+ if (callee) {
176
+ const key = calleeKey(callee);
177
+ if (key)
178
+ addCall(key, node);
179
+ }
180
+ }
181
+ for (const child of namedChildren(node))
182
+ walk(child);
183
+ };
184
+ for (const stmt of statements)
185
+ walk(stmt);
186
+ return steps;
187
+ }
188
+ function handleFunction(file, node, functions) {
189
+ const name = functionName(node);
190
+ if (!name)
191
+ return;
192
+ const body = childByType(node, "compound_statement");
193
+ functions.push({
194
+ key: name,
195
+ label: `${name}${getParamsLabel(node)}`,
196
+ params: declaredParamsOf(node),
197
+ file,
198
+ steps: body ? collectStatements(file, namedChildren(body)) : [],
199
+ exported: !hasStatic(node),
200
+ start: node.startIndex,
201
+ end: node.endIndex,
202
+ });
203
+ }
204
+ function extractFromTree(file, _source, tree) {
205
+ const functions = [];
206
+ for (const stmt of namedChildren(tree.rootNode)) {
207
+ if (stmt.type === "function_definition") {
208
+ handleFunction(file, stmt, functions);
209
+ }
210
+ }
211
+ return functions;
212
+ }
213
+ export const cExtractor = {
214
+ id: "c",
215
+ extensions: [".c", ".h"],
216
+ grammarPackage: "tree-sitter-c",
217
+ extract: extractFromTree,
218
+ };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * AST-side call metadata shared by every extractor: declared parameter lists,
3
+ * the arguments as written, lexical scopes for target classification, and the
4
+ * binder that pairs arguments with parameter slots.
5
+ *
6
+ * Nothing here invents a binding. A parameter slot is only named when the AST
7
+ * says so, and every lossy step records a reason from {@link CALL_REASONS}
8
+ * (spread, destructuring, unknown parameter list) instead of guessing.
9
+ */
10
+ import { type CallArgument, type CallArgumentMapping, type CallArgumentSyntax, type CallContext, type CallSyntax, type CallTargetKind, type DeclaredParams } from "../types.js";
11
+ import { type SyntaxNode } from "./types.js";
12
+ /**
13
+ * Source text as written, cut to {@link MAX_ARGUMENT_CHARS}. The kept bytes are
14
+ * untouched: no collapsing, trimming, or re-quoting, so a truncated expression
15
+ * is still a byte-exact prefix of the source.
16
+ */
17
+ export declare function cappedText(raw: string): Pick<CallArgument, "expression" | "truncated" | "originalLength">;
18
+ /** Verbatim parameter list, parenthesised when the grammar omits the parens. */
19
+ export declare function parameterText(params: SyntaxNode | null): string;
20
+ /** Parameters known by text only: no slots, so binding stays unknown. */
21
+ export declare function textOnlyParams(text: string): DeclaredParams;
22
+ /** Declared parameters of a JS/TS function, arrow, or method node. */
23
+ export declare function jsParameterList(params: SyntaxNode | null): DeclaredParams | undefined;
24
+ /** Declared parameters of a Python function or lambda node. */
25
+ export declare function pythonParameterList(params: SyntaxNode | null): DeclaredParams | undefined;
26
+ export interface ArgumentReadResult {
27
+ args: CallArgumentSyntax[];
28
+ /** Number of written arguments omitted by the extraction limit. */
29
+ omitted: number;
30
+ }
31
+ /**
32
+ * Syntax for a call in a grammar without scope analysis: the target is the
33
+ * qualified name the extractor recognised, so its association with a definition
34
+ * stays a heuristic (`candidate`) and arguments are never matched to parameters.
35
+ * The arguments themselves are still read as written, because they come from
36
+ * the call node and are not inferred.
37
+ */
38
+ export declare function heuristicCallSyntax(callNode: SyntaxNode, callee: string): CallSyntax;
39
+ export interface BoundArguments {
40
+ mapping: CallArgumentMapping;
41
+ /** Parameter name per argument, aligned with the argument list. */
42
+ bound: (string | null)[];
43
+ reasons: string[];
44
+ /** At least one argument has no reliable parameter. */
45
+ partial: boolean;
46
+ }
47
+ /**
48
+ * Pair written arguments with declared slots. A slot is filled at most once and
49
+ * only by a name the AST actually declares; anything else leaves the argument
50
+ * unbound and marks the result partial with the reason why.
51
+ */
52
+ export declare function bindArguments(params: DeclaredParams, args: readonly CallArgumentSyntax[]): BoundArguments;
53
+ /** What the call tree learned about the callee's definition, if anything. */
54
+ export interface CallResolution {
55
+ /** A definition was resolved for this call. */
56
+ resolved: boolean;
57
+ /** The resolved definition matches the lexical parameter AST in this file. */
58
+ lexical: boolean;
59
+ }
60
+ /**
61
+ * The public contract for one call site: the target expression as written, the
62
+ * callee's declared list, and the arguments with their bindings.
63
+ *
64
+ * A candidate target keeps `mapping: "unknown"`: the association is a name
65
+ * heuristic, so its bindings are never assumed.
66
+ */
67
+ export declare function callContextFromSyntax(syntax: CallSyntax, params: DeclaredParams | undefined, resolution: CallResolution): CallContext;
68
+ /** Syntax for a target the extractor cannot associate with any declaration. */
69
+ export declare function dynamicSyntax(callee: string, args: ArgumentReadResult | undefined, reasons?: readonly string[]): CallSyntax;
70
+ /** Syntax for a target whose binding is a name/member heuristic. */
71
+ export declare function candidateSyntax(callee: string, args: ArgumentReadResult | undefined, reasons?: readonly string[]): CallSyntax;
72
+ /** Syntax for a target a declaration in this file actually binds. */
73
+ export declare function lexicalSyntax(callee: string, params: DeclaredParams | undefined, args: ArgumentReadResult | undefined, reasons?: readonly string[]): CallSyntax;
74
+ /** One binding a lexical analysis found in the source. */
75
+ export interface LocalBinding {
76
+ callable: boolean;
77
+ params?: DeclaredParams;
78
+ kind: "function" | "param" | "local" | "import" | "class";
79
+ }
80
+ /** A lexical scope; lookups walk outward through `parent`. */
81
+ export interface Scope {
82
+ bindings: Map<string, LocalBinding>;
83
+ parent: Scope | null;
84
+ /** File-local syntactic writes, not inferred execution order or values. */
85
+ writes: Set<string>;
86
+ }
87
+ export declare function newScope(parent?: Scope | null): Scope;
88
+ export declare function scopeLookup(scope: Scope | null, name: string): LocalBinding | undefined;
89
+ /** Params and body declarations of one JS function/arrow/method node. */
90
+ export declare function jsFunctionScope(node: SyntaxNode, parent: Scope | null): Scope;
91
+ /**
92
+ * Module bindings of a JS/TS file: declarations and imports. Each statement is
93
+ * classified on its own, and an `export` is classified as what it declares.
94
+ */
95
+ export declare function jsModuleScope(root: SyntaxNode): Scope;
96
+ export interface CalleeTarget {
97
+ callee: string;
98
+ target: CallTargetKind;
99
+ params?: DeclaredParams;
100
+ reasons: string[];
101
+ }
102
+ /** Everything a JS/TS extraction knows at one call site. */
103
+ export interface JsEnv {
104
+ scope: Scope;
105
+ className: string | null;
106
+ }
107
+ /**
108
+ * Classify plain, unambiguous declarations lexically. Imports and member
109
+ * dispatch remain candidates because this extractor does not resolve them.
110
+ */
111
+ export declare function classifyJsCallee(node: SyntaxNode, env: JsEnv): CalleeTarget;
112
+ /** Classified target plus arguments of one JS/TS call site. */
113
+ export declare function jsCallSyntax(calleeNode: SyntaxNode | null, argsNode: SyntaxNode | null, env: JsEnv): CallSyntax | undefined;
114
+ /** Params and body assignments of one Python function or lambda node. */
115
+ export declare function pythonFunctionScope(node: SyntaxNode, parent: Scope | null): Scope;
116
+ /** Module bindings of a Python file: defs, classes, imports, assignments. */
117
+ export declare function pythonModuleScope(root: SyntaxNode): Scope;
118
+ export interface PythonEnv {
119
+ scope: Scope;
120
+ className: string | null;
121
+ }
122
+ /** Python imports, decorators, and member dispatch do not establish a target. */
123
+ export declare function classifyPythonCallee(node: SyntaxNode, env: PythonEnv): CalleeTarget;
124
+ /** Classified target plus arguments of one Python call site. */
125
+ export declare function pythonCallSyntax(calleeNode: SyntaxNode | null, argsNode: SyntaxNode | null, env: PythonEnv): CallSyntax | undefined;