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,307 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, declaredParamsOf, collapseWs, locFromNode, namedChildren, } from "./types.js";
3
+ function isPrivate(node) {
4
+ return /\bprivate\b|\bprotected\b/.test(namedChildren(node)
5
+ .filter((c) => c.type === "modifiers" || c.type === "access_modifier")
6
+ .map((c) => c.text)
7
+ .join(" ") || node.text.slice(0, 40));
8
+ }
9
+ function getParamsLabel(params) {
10
+ if (!params || params.type !== "parameters")
11
+ return "()";
12
+ const names = [];
13
+ for (const p of namedChildren(params)) {
14
+ if (p.type === "parameter") {
15
+ const id = childByType(p, "identifier");
16
+ names.push(id?.text ?? "_");
17
+ }
18
+ }
19
+ return names.length === 0 ? "()" : `(${names.join(", ")})`;
20
+ }
21
+ function isLikelyTypeName(name) {
22
+ const c = name[0];
23
+ return c !== undefined && c === c.toUpperCase() && c !== c.toLowerCase();
24
+ }
25
+ function calleeKey(node, typeName) {
26
+ if (node.type === "identifier") {
27
+ // Services() → constructor-style call (common Scala apply sugar)
28
+ if (isLikelyTypeName(node.text))
29
+ return `new ${node.text}`;
30
+ // Bare calls inside a class/object refer to siblings: prepare() → Runner.prepare
31
+ if (typeName)
32
+ return `${typeName}.${node.text}`;
33
+ return node.text;
34
+ }
35
+ if (node.type === "field_expression") {
36
+ const object = node.namedChild(0);
37
+ const field = node.namedChild(1);
38
+ if (!object || !field)
39
+ return null;
40
+ const prop = field.text;
41
+ if (object.type === "identifier") {
42
+ if ((object.text === "this" || object.text === "self") && typeName) {
43
+ return `${typeName}.${prop}`;
44
+ }
45
+ return `${object.text}.${prop}`;
46
+ }
47
+ if (typeName)
48
+ return `${typeName}.${prop}`;
49
+ return prop;
50
+ }
51
+ return null;
52
+ }
53
+ function statementsOf(body) {
54
+ if (!body)
55
+ return [];
56
+ if (body.type === "block")
57
+ return namedChildren(body);
58
+ return [body];
59
+ }
60
+ function collectStatements(file, statements, typeName) {
61
+ const steps = [];
62
+ const seen = new Set();
63
+ const addCall = (key, node) => {
64
+ const mark = `${key}:${node.startIndex}`;
65
+ if (seen.has(mark))
66
+ return;
67
+ seen.add(mark);
68
+ const step = { type: "call", key, ...locFromNode(file, node) };
69
+ step.syntax = heuristicCallSyntax(node, key);
70
+ steps.push(step);
71
+ };
72
+ const pushIfChain = (node, asElseIf) => {
73
+ const kids = namedChildren(node);
74
+ const blocks = kids.filter((c) => c.type === "block");
75
+ const nestedIf = kids.find((c) => c.type === "if_expression") ?? null;
76
+ const cond = childByType(node, "parenthesized_expression") ??
77
+ kids.find((c) => c.type !== "block" && c.type !== "if_expression") ??
78
+ null;
79
+ const condInner = cond?.type === "parenthesized_expression"
80
+ ? (cond.namedChild(0) ?? cond)
81
+ : cond;
82
+ const condText = condInner ? collapseWs(condInner.text) : "";
83
+ const kind = asElseIf ? "else-if" : "if";
84
+ const labelKind = asElseIf ? "else if" : "if";
85
+ steps.push({
86
+ type: "branch",
87
+ key: condText ? `${kind}:${condText}` : kind,
88
+ label: condText ? `${labelKind} (${condText})` : labelKind,
89
+ ...locFromNode(file, condInner ?? node),
90
+ children: collectStatements(file, statementsOf(blocks[0] ?? null), typeName),
91
+ });
92
+ if (nestedIf) {
93
+ pushIfChain(nestedIf, true);
94
+ return;
95
+ }
96
+ if (blocks[1]) {
97
+ steps.push({
98
+ type: "branch",
99
+ key: "else",
100
+ label: "else",
101
+ ...locFromNode(file, blocks[1]),
102
+ children: collectStatements(file, statementsOf(blocks[1]), typeName),
103
+ });
104
+ }
105
+ };
106
+ const walk = (node) => {
107
+ if (node.type === "function_definition" ||
108
+ node.type === "class_definition" ||
109
+ node.type === "object_definition" ||
110
+ node.type === "trait_definition" ||
111
+ node.type === "lambda_expression") {
112
+ return;
113
+ }
114
+ if (node.type === "if_expression") {
115
+ pushIfChain(node, false);
116
+ return;
117
+ }
118
+ if (node.type === "try_expression") {
119
+ const tryBlock = childByType(node, "block");
120
+ steps.push({
121
+ type: "branch",
122
+ key: "try",
123
+ label: "try",
124
+ ...locFromNode(file, node),
125
+ children: collectStatements(file, statementsOf(tryBlock), typeName),
126
+ });
127
+ for (const clause of namedChildren(node)) {
128
+ if (clause.type === "catch_clause") {
129
+ const caseBlock = childByType(clause, "case_block");
130
+ const cases = caseBlock ? namedChildren(caseBlock) : [];
131
+ if (cases.length === 0) {
132
+ steps.push({
133
+ type: "branch",
134
+ key: "catch",
135
+ label: "catch",
136
+ ...locFromNode(file, clause),
137
+ children: collectStatements(file, namedChildren(clause), typeName),
138
+ });
139
+ }
140
+ else {
141
+ for (const c of cases) {
142
+ if (c.type !== "case_clause")
143
+ continue;
144
+ const pattern = namedChildren(c).find((x) => x.type !== "block" &&
145
+ x.type !== "call_expression" &&
146
+ x.type !== "infix_expression") ?? namedChildren(c)[0] ?? null;
147
+ const text = pattern ? collapseWs(pattern.text) : "";
148
+ const bodyNodes = namedChildren(c).slice(1);
149
+ steps.push({
150
+ type: "branch",
151
+ key: text ? `catch:${text}` : "catch",
152
+ label: text ? `catch ${text}` : "catch",
153
+ ...locFromNode(file, pattern ?? c),
154
+ children: collectStatements(file, bodyNodes, typeName),
155
+ });
156
+ }
157
+ }
158
+ }
159
+ if (clause.type === "finally_clause") {
160
+ const body = childByType(clause, "block") ?? clause.namedChild(0) ?? null;
161
+ steps.push({
162
+ type: "branch",
163
+ key: "finally",
164
+ label: "finally",
165
+ ...locFromNode(file, clause),
166
+ children: collectStatements(file, statementsOf(body), typeName),
167
+ });
168
+ }
169
+ }
170
+ return;
171
+ }
172
+ if (node.type === "match_expression") {
173
+ const caseBlock = childByType(node, "case_block");
174
+ for (const clause of caseBlock ? namedChildren(caseBlock) : []) {
175
+ if (clause.type !== "case_clause")
176
+ continue;
177
+ const kids = namedChildren(clause);
178
+ const pattern = kids[0] ?? null;
179
+ const text = pattern ? collapseWs(pattern.text) : "";
180
+ const bodyNodes = kids.slice(1);
181
+ steps.push({
182
+ type: "branch",
183
+ key: text ? `case:${text}` : "case",
184
+ label: text ? `case ${text}` : "case",
185
+ ...locFromNode(file, pattern ?? clause),
186
+ children: collectStatements(file, bodyNodes, typeName),
187
+ });
188
+ }
189
+ return;
190
+ }
191
+ if (node.type === "call_expression") {
192
+ const callee = node.namedChild(0);
193
+ if (callee) {
194
+ const key = calleeKey(callee, typeName);
195
+ if (key)
196
+ addCall(key, node);
197
+ }
198
+ for (const child of namedChildren(node).slice(1))
199
+ walk(child);
200
+ return;
201
+ }
202
+ if (node.type === "instance_expression") {
203
+ // new Foo(args)
204
+ const typeId = childByType(node, "type_identifier");
205
+ if (typeId)
206
+ addCall(`new ${typeId.text}`, node);
207
+ for (const child of namedChildren(node))
208
+ walk(child);
209
+ return;
210
+ }
211
+ for (const child of namedChildren(node))
212
+ walk(child);
213
+ };
214
+ for (const stmt of statements)
215
+ walk(stmt);
216
+ return steps;
217
+ }
218
+ function handleFunction(file, node, typeName, functions) {
219
+ const name = childByType(node, "identifier")?.text ?? null;
220
+ if (!name)
221
+ return;
222
+ const params = childByType(node, "parameters");
223
+ const body = childByType(node, "block") ??
224
+ namedChildren(node).find((c) => c.type !== "identifier" &&
225
+ c.type !== "parameters" &&
226
+ c.type !== "type_identifier" &&
227
+ c.type !== "modifiers" &&
228
+ c.type !== "access_modifier" &&
229
+ c.type !== "singleton_type") ??
230
+ null;
231
+ const key = typeName ? `${typeName}.${name}` : name;
232
+ const exported = !isPrivate(node);
233
+ const steps = collectStatements(file, statementsOf(body), typeName);
234
+ functions.push({
235
+ key,
236
+ label: `${key}${getParamsLabel(params)}`,
237
+ params: declaredParamsOf(node),
238
+ file,
239
+ steps,
240
+ exported,
241
+ start: node.startIndex,
242
+ end: node.endIndex,
243
+ });
244
+ // object Foo { def apply(...) } → also index as constructor alias
245
+ if (typeName && name === "apply") {
246
+ functions.push({
247
+ key: `new ${typeName}`,
248
+ label: `${typeName}${getParamsLabel(params)}`,
249
+ params: declaredParamsOf(node),
250
+ file,
251
+ steps,
252
+ exported,
253
+ start: node.startIndex,
254
+ end: node.endIndex,
255
+ });
256
+ }
257
+ // class Foo { def this(...) } → secondary constructor alias
258
+ if (typeName && name === "this") {
259
+ functions.push({
260
+ key: `new ${typeName}`,
261
+ label: `${typeName}${getParamsLabel(params)}`,
262
+ file,
263
+ steps,
264
+ exported,
265
+ start: node.startIndex,
266
+ end: node.endIndex,
267
+ });
268
+ }
269
+ }
270
+ function handleTemplate(file, node, functions) {
271
+ const typeName = childByType(node, "identifier")?.text ?? null;
272
+ if (!typeName)
273
+ return;
274
+ const body = childByType(node, "template_body");
275
+ if (!body)
276
+ return;
277
+ for (const element of namedChildren(body)) {
278
+ if (element.type === "function_definition") {
279
+ handleFunction(file, element, typeName, functions);
280
+ }
281
+ else if (element.type === "class_definition" ||
282
+ element.type === "object_definition" ||
283
+ element.type === "trait_definition") {
284
+ handleTemplate(file, element, functions);
285
+ }
286
+ }
287
+ }
288
+ function extractFromTree(file, _source, tree) {
289
+ const functions = [];
290
+ for (const stmt of namedChildren(tree.rootNode)) {
291
+ if (stmt.type === "function_definition") {
292
+ handleFunction(file, stmt, null, functions);
293
+ }
294
+ else if (stmt.type === "class_definition" ||
295
+ stmt.type === "object_definition" ||
296
+ stmt.type === "trait_definition") {
297
+ handleTemplate(file, stmt, functions);
298
+ }
299
+ }
300
+ return functions;
301
+ }
302
+ export const scalaExtractor = {
303
+ id: "scala",
304
+ extensions: [".scala"],
305
+ grammarPackage: "tree-sitter-scala",
306
+ extract: extractFromTree,
307
+ };
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const solidityExtractor: LanguageExtractor;
@@ -0,0 +1,240 @@
1
+ import { heuristicCallSyntax } from "./call-syntax.js";
2
+ import { childByType, collapseWs, locFromNode, namedChildren, } from "./types.js";
3
+ function getParamsLabel(fn) {
4
+ const names = [];
5
+ for (const c of namedChildren(fn)) {
6
+ if (c.type !== "parameter")
7
+ continue;
8
+ const id = childByType(c, "identifier");
9
+ names.push(id?.text ?? "_");
10
+ }
11
+ return names.length === 0 ? "()" : `(${names.join(", ")})`;
12
+ }
13
+ function typeNameFrom(node) {
14
+ if (!node)
15
+ return null;
16
+ if (node.type === "identifier")
17
+ return node.text;
18
+ if (node.type === "user_defined_type" || node.type === "type_name") {
19
+ const id = childByType(node, "identifier") ?? childByType(node, "user_defined_type");
20
+ if (id?.type === "identifier")
21
+ return id.text;
22
+ return typeNameFrom(id);
23
+ }
24
+ const id = childByType(node, "identifier");
25
+ return id?.text ?? null;
26
+ }
27
+ function calleeKey(node, contractName) {
28
+ if (node.type === "identifier") {
29
+ // Bare call inside contract → Contract.fn (local)
30
+ if (contractName)
31
+ return `${contractName}.${node.text}`;
32
+ return node.text;
33
+ }
34
+ if (node.type === "member_expression") {
35
+ const object = node.namedChild(0);
36
+ const prop = node.namedChild(1);
37
+ if (!prop)
38
+ return null;
39
+ const propName = prop.text;
40
+ if (object?.type === "identifier") {
41
+ if (object.text === "this" && contractName) {
42
+ return `${contractName}.${propName}`;
43
+ }
44
+ return `${object.text}.${propName}`;
45
+ }
46
+ if (contractName)
47
+ return `${contractName}.${propName}`;
48
+ return propName;
49
+ }
50
+ if (node.type === "new_expression") {
51
+ const t = typeNameFrom(childByType(node, "type_name")) ??
52
+ typeNameFrom(childByType(node, "user_defined_type")) ??
53
+ typeNameFrom(node.namedChild(0));
54
+ return t ? `new ${t}` : null;
55
+ }
56
+ return null;
57
+ }
58
+ function statementsOf(body) {
59
+ if (!body)
60
+ return [];
61
+ if (body.type === "function_body" || body.type === "block_statement") {
62
+ return namedChildren(body);
63
+ }
64
+ if (body.type === "statement") {
65
+ return namedChildren(body);
66
+ }
67
+ return [body];
68
+ }
69
+ function unwrapStatement(stmt) {
70
+ if (stmt.type === "statement" && stmt.namedChildCount === 1) {
71
+ return stmt.namedChild(0) ?? stmt;
72
+ }
73
+ return stmt;
74
+ }
75
+ function collectStatements(file, statements, contractName) {
76
+ const steps = [];
77
+ const seen = new Set();
78
+ const addCall = (key, node) => {
79
+ const mark = `${key}:${node.startIndex}`;
80
+ if (seen.has(mark))
81
+ return;
82
+ seen.add(mark);
83
+ const step = { type: "call", key, ...locFromNode(file, node) };
84
+ step.syntax = heuristicCallSyntax(node, key);
85
+ steps.push(step);
86
+ };
87
+ const walk = (node) => {
88
+ if (node.type === "function_definition" ||
89
+ node.type === "constructor_definition" ||
90
+ node.type === "modifier_definition" ||
91
+ node.type === "contract_declaration" ||
92
+ node.type === "library_declaration" ||
93
+ node.type === "interface_declaration") {
94
+ return;
95
+ }
96
+ if (node.type === "if_statement") {
97
+ const kids = namedChildren(node);
98
+ const cond = kids.find((c) => c.type !== "statement" &&
99
+ c.type !== "block_statement") ?? kids[0] ?? null;
100
+ const bodies = kids.filter((c) => c.type === "statement" || c.type === "block_statement");
101
+ const condText = cond ? collapseWs(cond.text) : "";
102
+ const thenNode = bodies[0] ? unwrapStatement(bodies[0]) : null;
103
+ const elseNode = bodies[1] ? unwrapStatement(bodies[1]) : null;
104
+ steps.push({
105
+ type: "branch",
106
+ key: condText ? `if:${condText}` : "if",
107
+ label: condText ? `if ${condText}` : "if",
108
+ ...locFromNode(file, cond ?? node),
109
+ children: thenNode
110
+ ? collectStatements(file, statementsOf(thenNode), contractName)
111
+ : [],
112
+ });
113
+ if (elseNode) {
114
+ if (elseNode.type === "if_statement") {
115
+ const nested = collectStatements(file, [elseNode], contractName);
116
+ for (const s of nested) {
117
+ if (s.type === "branch" && (s.key === "if" || s.key.startsWith("if:"))) {
118
+ steps.push({
119
+ ...s,
120
+ key: s.key === "if" ? "else-if" : s.key.replace(/^if:/, "else-if:"),
121
+ label: s.label.replace(/^if /, "else if "),
122
+ });
123
+ }
124
+ else {
125
+ steps.push(s);
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ steps.push({
131
+ type: "branch",
132
+ key: "else",
133
+ label: "else",
134
+ ...locFromNode(file, elseNode),
135
+ children: collectStatements(file, statementsOf(elseNode), contractName),
136
+ });
137
+ }
138
+ }
139
+ return;
140
+ }
141
+ if (node.type === "call_expression") {
142
+ const callee = childByType(node, "expression")?.namedChild?.(0) ??
143
+ childByType(node, "expression") ??
144
+ node.namedChild(0);
145
+ // expression → identifier | member_expression | new_expression
146
+ let target = callee;
147
+ if (callee?.type === "expression") {
148
+ target = callee.namedChild(0) ?? callee;
149
+ }
150
+ if (target) {
151
+ const key = calleeKey(target, contractName);
152
+ if (key)
153
+ addCall(key, node);
154
+ }
155
+ for (const child of namedChildren(node).slice(1))
156
+ walk(child);
157
+ return;
158
+ }
159
+ for (const child of namedChildren(node))
160
+ walk(child);
161
+ };
162
+ for (const stmt of statements)
163
+ walk(unwrapStatement(stmt));
164
+ return steps;
165
+ }
166
+ function handleFunction(file, node, contractName, functions) {
167
+ const name = childByType(node, "identifier")?.text ?? null;
168
+ if (!name)
169
+ return;
170
+ const body = childByType(node, "function_body");
171
+ const key = contractName ? `${contractName}.${name}` : name;
172
+ const visibility = childByType(node, "visibility")?.text;
173
+ functions.push({
174
+ key,
175
+ label: `${key}${getParamsLabel(node)}`,
176
+ file,
177
+ steps: body
178
+ ? collectStatements(file, statementsOf(body), contractName)
179
+ : [],
180
+ exported: visibility !== "private" && visibility !== "internal",
181
+ start: node.startIndex,
182
+ end: node.endIndex,
183
+ });
184
+ }
185
+ function handleConstructor(file, node, contractName, functions) {
186
+ const body = childByType(node, "function_body");
187
+ const info = {
188
+ key: `${contractName}.constructor`,
189
+ label: `new ${contractName}${getParamsLabel(node)}`,
190
+ file,
191
+ steps: body
192
+ ? collectStatements(file, statementsOf(body), contractName)
193
+ : [],
194
+ exported: true,
195
+ start: node.startIndex,
196
+ end: node.endIndex,
197
+ };
198
+ functions.push(info);
199
+ functions.push({
200
+ ...info,
201
+ key: `new ${contractName}`,
202
+ label: `new ${contractName}()`,
203
+ });
204
+ }
205
+ function handleContractLike(file, node, functions) {
206
+ const name = childByType(node, "identifier")?.text ?? null;
207
+ if (!name)
208
+ return;
209
+ const body = childByType(node, "contract_body") ?? childByType(node, "interface_body");
210
+ if (!body)
211
+ return;
212
+ for (const child of namedChildren(body)) {
213
+ if (child.type === "function_definition") {
214
+ handleFunction(file, child, name, functions);
215
+ }
216
+ else if (child.type === "constructor_definition") {
217
+ handleConstructor(file, child, name, functions);
218
+ }
219
+ }
220
+ }
221
+ function extractFromTree(file, _source, tree) {
222
+ const functions = [];
223
+ for (const stmt of namedChildren(tree.rootNode)) {
224
+ if (stmt.type === "contract_declaration" ||
225
+ stmt.type === "library_declaration" ||
226
+ stmt.type === "interface_declaration") {
227
+ handleContractLike(file, stmt, functions);
228
+ }
229
+ else if (stmt.type === "function_definition") {
230
+ handleFunction(file, stmt, null, functions);
231
+ }
232
+ }
233
+ return functions;
234
+ }
235
+ export const solidityExtractor = {
236
+ id: "solidity",
237
+ extensions: [".sol"],
238
+ grammarPackage: "tree-sitter-solidity",
239
+ extract: extractFromTree,
240
+ };
@@ -0,0 +1,2 @@
1
+ import { type LanguageExtractor } from "./types.js";
2
+ export declare const swiftExtractor: LanguageExtractor;