clast-ai 0.1.2 → 0.2.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 (290) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +11 -9
  3. package/README.md +447 -401
  4. package/bench/ab/onair-concept.json +22 -0
  5. package/bench/ab/onair-deadcode.json +7 -0
  6. package/bench/ab/onair-hard.json +32 -0
  7. package/bench/ab/onair-impact.json +17 -0
  8. package/bench/ab/onair-refactors.json +18 -0
  9. package/bench/ab/onair-survey.json +17 -0
  10. package/bench/ab/onair-tasks.json +20 -0
  11. package/bench/ab/onair.json +52 -0
  12. package/bench/ab/vscode-concept.json +22 -0
  13. package/bench/ab/vscode-hard.json +32 -0
  14. package/bench/ab/vscode-impact.json +17 -0
  15. package/bench/ab/vscode-refactors.json +18 -0
  16. package/bench/ab/vscode-tasks.json +20 -0
  17. package/bench/ab/vscode.json +52 -0
  18. package/bench/ab.mjs +213 -0
  19. package/bench/bench.mjs +98 -0
  20. package/bench/resolution.mjs +75 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +257 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/config/defaults.d.ts +12 -2
  25. package/dist/config/defaults.js +38 -31
  26. package/dist/config/defaults.js.map +1 -1
  27. package/dist/config/index.d.ts +1 -1
  28. package/dist/config/index.js +26 -14
  29. package/dist/config/index.js.map +1 -1
  30. package/dist/db/index.d.ts +73 -13
  31. package/dist/db/index.js +481 -39
  32. package/dist/db/index.js.map +1 -1
  33. package/dist/engine.d.ts +15 -0
  34. package/dist/engine.js +28 -0
  35. package/dist/engine.js.map +1 -0
  36. package/dist/indexer/imports.d.ts +6 -0
  37. package/dist/indexer/imports.js +254 -0
  38. package/dist/indexer/imports.js.map +1 -0
  39. package/dist/indexer/index.d.ts +63 -0
  40. package/dist/indexer/index.js +360 -0
  41. package/dist/indexer/index.js.map +1 -0
  42. package/dist/indexer/process.d.ts +22 -0
  43. package/dist/indexer/process.js +42 -0
  44. package/dist/indexer/process.js.map +1 -0
  45. package/dist/indexer/scan.d.ts +43 -0
  46. package/dist/indexer/scan.js +223 -0
  47. package/dist/indexer/scan.js.map +1 -0
  48. package/dist/indexer/worker.d.ts +1 -0
  49. package/dist/indexer/worker.js +23 -0
  50. package/dist/indexer/worker.js.map +1 -0
  51. package/dist/llm/comment-generator.d.ts +17 -19
  52. package/dist/llm/comment-generator.js +46 -80
  53. package/dist/llm/comment-generator.js.map +1 -1
  54. package/dist/llm/index.d.ts +0 -1
  55. package/dist/llm/index.js.map +1 -1
  56. package/dist/llm/prompts.d.ts +0 -1
  57. package/dist/llm/prompts.js +16 -16
  58. package/dist/parser/extractor.d.ts +21 -16
  59. package/dist/parser/extractor.js +700 -228
  60. package/dist/parser/extractor.js.map +1 -1
  61. package/dist/parser/index.d.ts +17 -21
  62. package/dist/parser/index.js +115 -171
  63. package/dist/parser/index.js.map +1 -1
  64. package/dist/parser/langs/c.d.ts +3 -0
  65. package/dist/parser/langs/c.js +879 -0
  66. package/dist/parser/langs/c.js.map +1 -0
  67. package/dist/parser/langs/csharp.d.ts +2 -0
  68. package/dist/parser/langs/csharp.js +548 -0
  69. package/dist/parser/langs/csharp.js.map +1 -0
  70. package/dist/parser/langs/dart.d.ts +2 -0
  71. package/dist/parser/langs/dart.js +893 -0
  72. package/dist/parser/langs/dart.js.map +1 -0
  73. package/dist/parser/langs/go.d.ts +2 -0
  74. package/dist/parser/langs/go.js +455 -0
  75. package/dist/parser/langs/go.js.map +1 -0
  76. package/dist/parser/langs/java.d.ts +2 -0
  77. package/dist/parser/langs/java.js +390 -0
  78. package/dist/parser/langs/java.js.map +1 -0
  79. package/dist/parser/langs/php.d.ts +2 -0
  80. package/dist/parser/langs/php.js +486 -0
  81. package/dist/parser/langs/php.js.map +1 -0
  82. package/dist/parser/langs/python.d.ts +2 -0
  83. package/dist/parser/langs/python.js +216 -0
  84. package/dist/parser/langs/python.js.map +1 -0
  85. package/dist/parser/langs/ruby.d.ts +2 -0
  86. package/dist/parser/langs/ruby.js +602 -0
  87. package/dist/parser/langs/ruby.js.map +1 -0
  88. package/dist/parser/langs/rust.d.ts +2 -0
  89. package/dist/parser/langs/rust.js +931 -0
  90. package/dist/parser/langs/rust.js.map +1 -0
  91. package/dist/parser/langs/swift.d.ts +2 -0
  92. package/dist/parser/langs/swift.js +616 -0
  93. package/dist/parser/langs/swift.js.map +1 -0
  94. package/dist/parser/langs/typescript.d.ts +4 -0
  95. package/dist/parser/langs/typescript.js +535 -0
  96. package/dist/parser/langs/typescript.js.map +1 -0
  97. package/dist/parser/registry.d.ts +16 -0
  98. package/dist/parser/registry.js +87 -0
  99. package/dist/parser/registry.js.map +1 -0
  100. package/dist/parser/returns.d.ts +9 -0
  101. package/dist/parser/returns.js +152 -0
  102. package/dist/parser/returns.js.map +1 -0
  103. package/dist/parser/spec.d.ts +165 -0
  104. package/dist/parser/spec.js +188 -0
  105. package/dist/parser/spec.js.map +1 -0
  106. package/dist/query/arity.d.ts +27 -0
  107. package/dist/query/arity.js +187 -0
  108. package/dist/query/arity.js.map +1 -0
  109. package/dist/query/callgraph.d.ts +113 -0
  110. package/dist/query/callgraph.js +332 -0
  111. package/dist/query/callgraph.js.map +1 -0
  112. package/dist/query/format.d.ts +36 -0
  113. package/dist/query/format.js +136 -0
  114. package/dist/query/format.js.map +1 -0
  115. package/dist/query/hierarchy.d.ts +44 -0
  116. package/dist/query/hierarchy.js +441 -0
  117. package/dist/query/hierarchy.js.map +1 -0
  118. package/dist/query/resolve.d.ts +265 -0
  119. package/dist/query/resolve.js +1957 -0
  120. package/dist/query/resolve.js.map +1 -0
  121. package/dist/query/search.d.ts +35 -0
  122. package/dist/query/search.js +379 -0
  123. package/dist/query/search.js.map +1 -0
  124. package/dist/query/words.d.ts +30 -0
  125. package/dist/query/words.js +201 -0
  126. package/dist/query/words.js.map +1 -0
  127. package/dist/server/index.d.ts +1 -3
  128. package/dist/server/index.js +313 -73
  129. package/dist/server/index.js.map +1 -1
  130. package/dist/tools/context.d.ts +35 -0
  131. package/dist/tools/context.js +79 -0
  132. package/dist/tools/context.js.map +1 -0
  133. package/dist/tools/explore.d.ts +51 -0
  134. package/dist/tools/explore.js +779 -0
  135. package/dist/tools/explore.js.map +1 -0
  136. package/dist/tools/impact.d.ts +21 -0
  137. package/dist/tools/impact.js +743 -0
  138. package/dist/tools/impact.js.map +1 -0
  139. package/dist/tools/search.d.ts +59 -0
  140. package/dist/tools/search.js +843 -0
  141. package/dist/tools/search.js.map +1 -0
  142. package/dist/tools/stats.d.ts +26 -0
  143. package/dist/tools/stats.js +604 -0
  144. package/dist/tools/stats.js.map +1 -0
  145. package/dist/types.d.ts +95 -40
  146. package/dist/types.js +9 -1
  147. package/dist/types.js.map +1 -1
  148. package/dist/version.d.ts +2 -0
  149. package/dist/version.js +3 -0
  150. package/dist/version.js.map +1 -0
  151. package/dist/watcher/index.d.ts +18 -16
  152. package/dist/watcher/index.js +94 -77
  153. package/dist/watcher/index.js.map +1 -1
  154. package/package.json +21 -24
  155. package/plugin/server.mjs +50 -0
  156. package/skills/audit/SKILL.md +37 -0
  157. package/skills/deadcode/SKILL.md +33 -0
  158. package/skills/explore/SKILL.md +54 -0
  159. package/skills/impact/SKILL.md +24 -0
  160. package/skills/status/SKILL.md +11 -0
  161. package/src/cli.ts +256 -0
  162. package/src/config/defaults.ts +50 -32
  163. package/src/config/index.ts +23 -14
  164. package/src/db/index.ts +540 -43
  165. package/src/engine.ts +30 -0
  166. package/src/indexer/imports.ts +241 -0
  167. package/src/indexer/index.ts +369 -0
  168. package/src/indexer/process.ts +63 -0
  169. package/src/indexer/scan.ts +226 -0
  170. package/src/indexer/worker.ts +24 -0
  171. package/src/llm/comment-generator.ts +52 -103
  172. package/src/llm/index.ts +80 -80
  173. package/src/llm/prompts.ts +95 -95
  174. package/src/parser/extractor.ts +643 -256
  175. package/src/parser/index.ts +105 -205
  176. package/src/parser/langs/c.ts +860 -0
  177. package/src/parser/langs/csharp.ts +519 -0
  178. package/src/parser/langs/dart.ts +820 -0
  179. package/src/parser/langs/go.ts +437 -0
  180. package/src/parser/langs/java.ts +361 -0
  181. package/src/parser/langs/php.ts +450 -0
  182. package/src/parser/langs/python.ts +206 -0
  183. package/src/parser/langs/ruby.ts +554 -0
  184. package/src/parser/langs/rust.ts +904 -0
  185. package/src/parser/langs/swift.ts +590 -0
  186. package/src/parser/langs/typescript.ts +499 -0
  187. package/src/parser/registry.ts +88 -0
  188. package/src/parser/returns.ts +137 -0
  189. package/src/parser/spec.ts +309 -0
  190. package/src/query/arity.ts +173 -0
  191. package/src/query/callgraph.ts +473 -0
  192. package/src/query/format.ts +136 -0
  193. package/src/query/hierarchy.ts +447 -0
  194. package/src/query/resolve.ts +1972 -0
  195. package/src/query/search.ts +430 -0
  196. package/src/query/words.ts +183 -0
  197. package/src/server/index.ts +390 -79
  198. package/src/tools/context.ts +82 -0
  199. package/src/tools/explore.ts +860 -0
  200. package/src/tools/impact.ts +748 -0
  201. package/src/tools/search.ts +886 -0
  202. package/src/tools/stats.ts +633 -0
  203. package/src/types.ts +113 -45
  204. package/src/version.ts +2 -0
  205. package/src/watcher/index.ts +82 -88
  206. package/tsconfig.json +7 -4
  207. package/.mcp.json +0 -8
  208. package/commands/clast-status.md +0 -9
  209. package/dist/config/defaults.d.ts.map +0 -1
  210. package/dist/config/index.d.ts.map +0 -1
  211. package/dist/db/index.d.ts.map +0 -1
  212. package/dist/db/queries.d.ts +0 -44
  213. package/dist/db/queries.d.ts.map +0 -1
  214. package/dist/db/queries.js +0 -251
  215. package/dist/db/queries.js.map +0 -1
  216. package/dist/db/schema.d.ts +0 -3
  217. package/dist/db/schema.d.ts.map +0 -1
  218. package/dist/db/schema.js +0 -94
  219. package/dist/db/schema.js.map +0 -1
  220. package/dist/db/types.d.ts +0 -44
  221. package/dist/db/types.d.ts.map +0 -1
  222. package/dist/db/types.js +0 -2
  223. package/dist/db/types.js.map +0 -1
  224. package/dist/llm/comment-generator.d.ts.map +0 -1
  225. package/dist/llm/index.d.ts.map +0 -1
  226. package/dist/llm/prompts.d.ts.map +0 -1
  227. package/dist/parser/call-resolver.d.ts +0 -15
  228. package/dist/parser/call-resolver.d.ts.map +0 -1
  229. package/dist/parser/call-resolver.js +0 -16
  230. package/dist/parser/call-resolver.js.map +0 -1
  231. package/dist/parser/extractor.d.ts.map +0 -1
  232. package/dist/parser/index.d.ts.map +0 -1
  233. package/dist/parser/languages.d.ts +0 -11
  234. package/dist/parser/languages.d.ts.map +0 -1
  235. package/dist/parser/languages.js +0 -74
  236. package/dist/parser/languages.js.map +0 -1
  237. package/dist/server/index.d.ts.map +0 -1
  238. package/dist/server/register-tools.d.ts +0 -7
  239. package/dist/server/register-tools.d.ts.map +0 -1
  240. package/dist/server/register-tools.js +0 -17
  241. package/dist/server/register-tools.js.map +0 -1
  242. package/dist/server/tools/clast-call-graph.d.ts +0 -4
  243. package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
  244. package/dist/server/tools/clast-call-graph.js +0 -70
  245. package/dist/server/tools/clast-call-graph.js.map +0 -1
  246. package/dist/server/tools/clast-comment.d.ts +0 -5
  247. package/dist/server/tools/clast-comment.d.ts.map +0 -1
  248. package/dist/server/tools/clast-comment.js +0 -64
  249. package/dist/server/tools/clast-comment.js.map +0 -1
  250. package/dist/server/tools/clast-file-summary.d.ts +0 -4
  251. package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
  252. package/dist/server/tools/clast-file-summary.js +0 -42
  253. package/dist/server/tools/clast-file-summary.js.map +0 -1
  254. package/dist/server/tools/clast-get-context.d.ts +0 -4
  255. package/dist/server/tools/clast-get-context.d.ts.map +0 -1
  256. package/dist/server/tools/clast-get-context.js +0 -80
  257. package/dist/server/tools/clast-get-context.js.map +0 -1
  258. package/dist/server/tools/clast-reindex.d.ts +0 -5
  259. package/dist/server/tools/clast-reindex.d.ts.map +0 -1
  260. package/dist/server/tools/clast-reindex.js +0 -36
  261. package/dist/server/tools/clast-reindex.js.map +0 -1
  262. package/dist/server/tools/clast-search.d.ts +0 -4
  263. package/dist/server/tools/clast-search.d.ts.map +0 -1
  264. package/dist/server/tools/clast-search.js +0 -29
  265. package/dist/server/tools/clast-search.js.map +0 -1
  266. package/dist/server/tools/clast-status.d.ts +0 -5
  267. package/dist/server/tools/clast-status.d.ts.map +0 -1
  268. package/dist/server/tools/clast-status.js +0 -23
  269. package/dist/server/tools/clast-status.js.map +0 -1
  270. package/dist/types.d.ts.map +0 -1
  271. package/dist/watcher/gitignore.d.ts +0 -9
  272. package/dist/watcher/gitignore.d.ts.map +0 -1
  273. package/dist/watcher/gitignore.js +0 -38
  274. package/dist/watcher/gitignore.js.map +0 -1
  275. package/dist/watcher/index.d.ts.map +0 -1
  276. package/skills/clast-explore/SKILL.md +0 -41
  277. package/src/db/queries.ts +0 -332
  278. package/src/db/schema.ts +0 -106
  279. package/src/db/types.ts +0 -47
  280. package/src/parser/call-resolver.ts +0 -17
  281. package/src/parser/languages.ts +0 -86
  282. package/src/server/register-tools.ts +0 -30
  283. package/src/server/tools/clast-call-graph.ts +0 -82
  284. package/src/server/tools/clast-comment.ts +0 -76
  285. package/src/server/tools/clast-file-summary.ts +0 -59
  286. package/src/server/tools/clast-get-context.ts +0 -97
  287. package/src/server/tools/clast-reindex.ts +0 -52
  288. package/src/server/tools/clast-search.ts +0 -38
  289. package/src/server/tools/clast-status.ts +0 -37
  290. package/src/watcher/gitignore.ts +0 -42
@@ -0,0 +1,187 @@
1
+ /** Languages whose signatures are checked. Others (PHP, Ruby, JavaScript, Swift, Dart) accept any count. */
2
+ export const ARITY_LANGS = new Set(['java', 'csharp', 'go', 'c', 'cpp', 'typescript', 'tsx', 'python', 'rust']);
3
+ function escapeRe(s) {
4
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5
+ }
6
+ /** Index of the `)` matching the `(` at `open`, or -1. Skips string literals. */
7
+ function closing(sig, open) {
8
+ let depth = 0;
9
+ for (let i = open; i < sig.length; i++) {
10
+ const ch = sig[i];
11
+ if (ch === '"' || ch === "'" || ch === '`') {
12
+ const end = sig.indexOf(ch, i + 1);
13
+ if (end === -1)
14
+ return -1;
15
+ i = end;
16
+ }
17
+ else if (ch === '(')
18
+ depth++;
19
+ else if (ch === ')' && --depth === 0)
20
+ return i;
21
+ }
22
+ return -1;
23
+ }
24
+ /** Split a parameter list on top-level commas. */
25
+ function splitParams(list) {
26
+ const out = [];
27
+ let depth = 0;
28
+ let start = 0;
29
+ for (let i = 0; i < list.length; i++) {
30
+ const ch = list[i];
31
+ if (ch === '"' || ch === "'" || ch === '`') {
32
+ const end = list.indexOf(ch, i + 1);
33
+ if (end === -1)
34
+ break;
35
+ i = end;
36
+ }
37
+ else if (ch === '(' || ch === '[' || ch === '{' || ch === '<')
38
+ depth++;
39
+ else if (ch === ')' || ch === ']' || ch === '}')
40
+ depth--;
41
+ else if (ch === '>') {
42
+ // Not the arrow of `=>` / `->`.
43
+ const prev = list[i - 1];
44
+ if (prev !== '=' && prev !== '-')
45
+ depth--;
46
+ }
47
+ else if (ch === ',' && depth <= 0) {
48
+ out.push(list.slice(start, i));
49
+ start = i + 1;
50
+ depth = 0;
51
+ }
52
+ }
53
+ out.push(list.slice(start));
54
+ return out.map(p => p.trim()).filter(Boolean);
55
+ }
56
+ /** True when the parameter has a default value (`x = 1`, `x: int = 1`) at its top level. */
57
+ function hasDefault(param) {
58
+ let depth = 0;
59
+ for (let i = 0; i < param.length; i++) {
60
+ const ch = param[i];
61
+ if (ch === '(' || ch === '[' || ch === '{' || ch === '<')
62
+ depth++;
63
+ else if (ch === ')' || ch === ']' || ch === '}' || (ch === '>' && param[i - 1] !== '=' && param[i - 1] !== '-'))
64
+ depth--;
65
+ else if (ch === '=' && depth <= 0) {
66
+ const next = param[i + 1];
67
+ const prev = param[i - 1];
68
+ if (next !== '=' && next !== '>' && prev !== '=' && prev !== '!' && prev !== '<' && prev !== '>')
69
+ return true;
70
+ }
71
+ }
72
+ return false;
73
+ }
74
+ export function paramRange(signature, name, lang) {
75
+ if (!ARITY_LANGS.has(lang) || !signature || !name)
76
+ return null;
77
+ let sig = signature;
78
+ if (lang === 'go') {
79
+ // func (r *T) Name(...): skip the receiver.
80
+ const recv = /^func\s*\(/.exec(sig);
81
+ if (recv) {
82
+ const end = closing(sig, recv[0].length - 1);
83
+ if (end === -1)
84
+ return null;
85
+ sig = sig.slice(end + 1);
86
+ }
87
+ }
88
+ const m = new RegExp('(?:^|[^\\w$])' + escapeRe(name) + '\\s*(?=[<\\[(])').exec(sig);
89
+ if (!m)
90
+ return null;
91
+ let i = m.index + m[0].length;
92
+ // Type parameters: <T, U> or Go [T any].
93
+ if (sig[i] === '<' || sig[i] === '[') {
94
+ const open = sig[i];
95
+ const close = open === '<' ? '>' : ']';
96
+ let depth = 0;
97
+ for (; i < sig.length; i++) {
98
+ if (sig[i] === open)
99
+ depth++;
100
+ else if (sig[i] === close && sig[i - 1] !== '=' && sig[i - 1] !== '-' && --depth === 0) {
101
+ i++;
102
+ break;
103
+ }
104
+ }
105
+ while (sig[i] === ' ')
106
+ i++;
107
+ }
108
+ if (sig[i] !== '(')
109
+ return null;
110
+ const end = closing(sig, i);
111
+ if (end === -1)
112
+ return null; // truncated signature
113
+ let params = splitParams(sig.slice(i + 1, end));
114
+ if (lang === 'c' || lang === 'cpp') {
115
+ if (params.length === 1 && params[0] === 'void')
116
+ params = [];
117
+ // `int f()` in C says nothing about the parameters.
118
+ else if (params.length === 0 && lang === 'c')
119
+ return null;
120
+ }
121
+ if (lang === 'python') {
122
+ if (params.length && /^(self|cls)\b/.test(params[0]))
123
+ params = params.slice(1);
124
+ }
125
+ else if (lang === 'rust') {
126
+ if (params.length && /^(&\s*('\w+\s+)?)?(mut\s+)?self\b/.test(params[0]))
127
+ params = params.slice(1);
128
+ }
129
+ else if (lang === 'typescript' || lang === 'tsx') {
130
+ if (params.length && /^this\s*:/.test(params[0]))
131
+ params = params.slice(1);
132
+ }
133
+ let min = 0;
134
+ let max = 0;
135
+ let variadic = false;
136
+ let ext = false;
137
+ let voidable = false;
138
+ for (let k = 0; k < params.length; k++) {
139
+ const p = params[k];
140
+ if (lang === 'python') {
141
+ if (p === '*' || p === '/')
142
+ continue; // keyword-only / positional-only markers
143
+ if (p.startsWith('*')) {
144
+ variadic = true;
145
+ continue;
146
+ }
147
+ }
148
+ if (p.includes('...') || (lang === 'csharp' && /^(\[[^\]]*\]\s*)*params\s/.test(p))) {
149
+ variadic = true;
150
+ continue;
151
+ }
152
+ if (lang === 'csharp' && k === 0 && /^(\[[^\]]*\]\s*)*this\s/.test(p))
153
+ ext = true;
154
+ max++;
155
+ const optional = hasDefault(p) ||
156
+ ((lang === 'typescript' || lang === 'tsx') && /^[\w$]+\?\s*(:|$)/.test(p));
157
+ if (!optional) {
158
+ min++;
159
+ voidable = (lang === 'typescript' || lang === 'tsx') && /:\s*(?:[A-Z]|T[A-Z]\w*)\s*$/.test(p);
160
+ }
161
+ }
162
+ return { min, max: variadic ? Infinity : max, ext, voidable };
163
+ }
164
+ /**
165
+ * Can a call with `argc` arguments be a call of a function with this range?
166
+ * `unbound` marks calls written through a type name (`Base.save(self, x)`,
167
+ * `Type::method(value, x)`), which pass the receiver as an argument.
168
+ */
169
+ export function arityFits(range, argc, lang, unbound) {
170
+ if (!range || argc < 0)
171
+ return true;
172
+ if (argc >= range.min && argc <= range.max)
173
+ return true;
174
+ // `emitter.fire()` on an Emitter<void>: a parameter of type T can be left out when T is void.
175
+ if (range.voidable && argc === range.min - 1)
176
+ return true;
177
+ // Extension method called as a member: the receiver fills the `this` parameter.
178
+ if (range.ext && argc + 1 >= range.min && argc + 1 <= range.max)
179
+ return true;
180
+ if (unbound && (lang === 'python' || lang === 'rust') && argc - 1 >= range.min && argc - 1 <= range.max)
181
+ return true;
182
+ // Go: f(g()) forwards all results of g.
183
+ if (lang === 'go' && argc === 1 && range.min > 1)
184
+ return true;
185
+ return false;
186
+ }
187
+ //# sourceMappingURL=arity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arity.js","sourceRoot":"","sources":["../../src/query/arity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import type { ClastDb } from '../db/index.js';
2
+ import { type SymbolRow } from '../types.js';
3
+ import { type Resolver } from './resolve.js';
4
+ export interface CallerEdge {
5
+ /** Calling symbol, or null for a call at file level. */
6
+ caller: SymbolRow | null;
7
+ path: string;
8
+ line: number;
9
+ count: number;
10
+ /** False when name-based resolution could not rule out another target. */
11
+ certain: boolean;
12
+ /**
13
+ * True for call sites whose receiver type is unknown and that could not be
14
+ * attributed to any definition: they MAY call the target (same method
15
+ * name), but there is no evidence either way.
16
+ */
17
+ weak?: boolean;
18
+ }
19
+ export interface CalleeEdge {
20
+ name: string;
21
+ line: number;
22
+ count: number;
23
+ receiver: string | null;
24
+ /** Resolved definition, or null for external / unknown callees. */
25
+ target: SymbolRow | null;
26
+ certain: boolean;
27
+ /** Number of other plausible definitions. */
28
+ alternatives: number;
29
+ }
30
+ /**
31
+ * The definition that best stands for a symbol's family member with the same
32
+ * qualified name: the function body rather than its prototype, the
33
+ * out-of-line C++ method rather than the in-class declaration.
34
+ */
35
+ export declare function representative(db: ClastDb, resolver: Resolver, sym: SymbolRow): SymbolRow;
36
+ /**
37
+ * Who calls `target`? Call sites are matched by name and then filtered by
38
+ * resolving each site back to its most plausible definition.
39
+ */
40
+ export interface CallerQuery {
41
+ /** Only call sites in files whose path contains this text (pushed down to SQL). */
42
+ pathContains?: string;
43
+ /** Examine fewer call sites (summaries that only show the first few callers). */
44
+ quick?: boolean;
45
+ /** Other overloads of the target whose call sites count too (a bare name means "any overload"). */
46
+ overloads?: SymbolRow[];
47
+ }
48
+ /** Set by callersOf when a name has more call sites than were examined. */
49
+ export interface CallerStats {
50
+ examined: number;
51
+ total: number;
52
+ }
53
+ export declare function callersOf(db: ClastDb, resolver: Resolver, target: SymbolRow, query?: CallerQuery, stats?: CallerStats): CallerEdge[];
54
+ /** What does `sym` call? For containers, the calls of all direct members. */
55
+ export declare function calleesOf(db: ClastDb, resolver: Resolver, sym: SymbolRow): CalleeEdge[];
56
+ /** One entry per calling symbol (call sites inside the same caller are merged). */
57
+ export interface CallerGroup {
58
+ caller: SymbolRow | null;
59
+ path: string;
60
+ /** First call line inside the caller. */
61
+ line: number;
62
+ /** Number of call sites in this caller. */
63
+ sites: number;
64
+ certain: boolean;
65
+ /** All call sites in this caller are unattributed (receiver type unknown). */
66
+ weak: boolean;
67
+ }
68
+ export declare function groupCallers(edges: CallerEdge[]): CallerGroup[];
69
+ /**
70
+ * Call sites of a name that has no definition in the project (an external
71
+ * library function, a dynamically defined method).
72
+ */
73
+ export declare function externalCallSites(db: ClastDb, name: string, limit?: number): CallerEdge[];
74
+ export interface GraphNode {
75
+ sym: SymbolRow | null;
76
+ /** For callers: where the call happens. For callees: call line in the parent. */
77
+ path: string;
78
+ line: number;
79
+ certain: boolean;
80
+ /** Call sites merged into this node. */
81
+ sites: number;
82
+ /** Unattributed call site (receiver type unknown). */
83
+ weak?: boolean;
84
+ label?: string;
85
+ children: GraphNode[];
86
+ /** Set when this symbol was already expanded elsewhere in the tree. */
87
+ repeated?: boolean;
88
+ /** Children omitted because of the node budget. */
89
+ omitted?: number;
90
+ /** Set when the node has too many edges to expand at this depth. */
91
+ hub?: number;
92
+ }
93
+ export interface GraphOptions {
94
+ depth: number;
95
+ /** Total node budget. */
96
+ budget: number;
97
+ includeTests: boolean;
98
+ /** Only edges whose file matches this path filter. */
99
+ path?: (path: string) => boolean;
100
+ /** Text every call-site path must contain (lets the database do the first cut for `within`). */
101
+ pathContains?: string;
102
+ /** Filled in for the root symbol: how many call sites of the name exist vs were examined. */
103
+ stats?: CallerStats;
104
+ /** Other overloads of the root whose callers are included. */
105
+ overloads?: SymbolRow[];
106
+ }
107
+ /**
108
+ * Breadth-first expansion of callers or callees, bounded by a total node
109
+ * budget so the output stays small. Hubs (symbols with dozens of callers)
110
+ * are reported but not expanded beyond the first level: their fan-out would
111
+ * drown the rest of the tree.
112
+ */
113
+ export declare function buildGraph(db: ClastDb, resolver: Resolver, root: SymbolRow, direction: 'callers' | 'callees', opts: GraphOptions): GraphNode;
@@ -0,0 +1,332 @@
1
+ import { CONTAINER_KINDS } from '../types.js';
2
+ import { isTestPath } from './format.js';
3
+ import { goInterfaceMethods } from './hierarchy.js';
4
+ import { langFamily } from './resolve.js';
5
+ const MAX_CALL_ROWS = 4000;
6
+ const MAX_NEAR_ROWS = 20000;
7
+ const QUICK_NEAR_ROWS = 5000;
8
+ const QUICK_FAR_ROWS = 1500;
9
+ const MAX_WEAK = 1500;
10
+ /**
11
+ * The definition that best stands for a symbol's family member with the same
12
+ * qualified name: the function body rather than its prototype, the
13
+ * out-of-line C++ method rather than the in-class declaration.
14
+ */
15
+ export function representative(db, resolver, sym) {
16
+ // Only C-family code separates declarations from definitions across files.
17
+ if (langFamily(sym.lang) !== 'c' && sym.kind !== 'declaration')
18
+ return sym;
19
+ let best = sym;
20
+ let bestScore = (sym.kind === 'declaration' ? 0 : 1000) + (sym.end_line - sym.start_line);
21
+ for (const id of resolver.familyOf(sym)) {
22
+ if (id === sym.id)
23
+ continue;
24
+ const other = db.symbolById(id);
25
+ // The same member in another file (header vs source); overloads in the same file are different functions.
26
+ if (!other || other.qname !== sym.qname || (other.file_id === sym.file_id && sym.kind !== 'declaration'))
27
+ continue;
28
+ const score = (other.kind === 'declaration' ? 0 : 1000) + (other.end_line - other.start_line);
29
+ if (score > bestScore) {
30
+ best = other;
31
+ bestScore = score;
32
+ }
33
+ }
34
+ return best;
35
+ }
36
+ /** Ids of the symbol itself plus (for containers) its direct members. */
37
+ function bodyIds(db, sym) {
38
+ if (CONTAINER_KINDS.has(sym.kind)) {
39
+ const members = db.all(`SELECT id FROM symbols WHERE parent_id = ?`, sym.id);
40
+ return [sym.id, ...members.map(m => m.id)];
41
+ }
42
+ // A function's body includes the closures and helpers nested inside it.
43
+ const ids = [sym.id];
44
+ let frontier = [sym.id];
45
+ for (let depth = 0; depth < 4 && frontier.length && ids.length < 200; depth++) {
46
+ const next = [];
47
+ for (const id of frontier) {
48
+ for (const row of db.all(`SELECT id FROM symbols WHERE parent_id = ? AND kind IN ('function', 'method')`, id)) {
49
+ ids.push(row.id);
50
+ next.push(row.id);
51
+ }
52
+ }
53
+ frontier = next;
54
+ }
55
+ return ids;
56
+ }
57
+ export function callersOf(db, resolver, target, query = {}, stats) {
58
+ const nameId = db.nameId(target.name, false);
59
+ if (nameId === undefined)
60
+ return [];
61
+ const cols = `c.rowid AS id, c.file_id, c.caller_id, c.line, c.receiver, c.rtype, c.via, c.argc, c.count, f.path, f.lang`;
62
+ const pathClause = query.pathContains ? ` AND f.path LIKE ? ESCAPE '\\'` : '';
63
+ const pathArg = query.pathContains ? ['%' + query.pathContains.replace(/[\\%_]/g, c => '\\' + c) + '%'] : [];
64
+ // Very common names (`dispose`, `push`, `get`) have more call sites than can be examined.
65
+ // Sites that can see the target - its own file and the files that import it - always go first.
66
+ const near = db.all(`SELECT ${cols} FROM calls c JOIN files f ON f.id = c.file_id
67
+ WHERE c.name_id = ? AND (c.file_id = ? OR c.file_id IN (SELECT file_id FROM imports WHERE resolved_id = ?))${pathClause}
68
+ LIMIT ${query.quick ? QUICK_NEAR_ROWS : MAX_NEAR_ROWS}`, nameId, target.file_id, target.file_id, ...pathArg);
69
+ const seenRows = new Set(near.map(r => r.id));
70
+ const far = db
71
+ .all(`SELECT ${cols} FROM calls c JOIN files f ON f.id = c.file_id
72
+ WHERE c.name_id = ?${pathClause} LIMIT ${MAX_CALL_ROWS + MAX_NEAR_ROWS}`, nameId, ...pathArg)
73
+ .filter(r => !seenRows.has(r.id))
74
+ .slice(0, query.quick ? QUICK_FAR_ROWS : MAX_CALL_ROWS);
75
+ const rows = [...near, ...far];
76
+ if (stats) {
77
+ stats.examined = rows.length;
78
+ stats.total = rows.length < (query.quick ? QUICK_FAR_ROWS : MAX_CALL_ROWS)
79
+ ? rows.length
80
+ : db.get(`SELECT COUNT(*) AS n FROM calls WHERE name_id = ?`, nameId)?.n ?? rows.length;
81
+ }
82
+ let family = resolver.familyOf(target);
83
+ // A constructor is called by naming its class: `new Foo(...)` resolves to the class.
84
+ if (target.parent_id !== null && target.kind === 'method') {
85
+ const parent = db.symbolById(target.parent_id);
86
+ if (parent && parent.name === target.name && CONTAINER_KINDS.has(parent.kind))
87
+ family = new Set([...family, parent.id]);
88
+ }
89
+ const arityTargets = query.overloads?.length ? [target, ...query.overloads] : [target];
90
+ /** Go interface methods the target satisfies structurally: calls through them are possible, not certain. */
91
+ let structural = null;
92
+ if (target.lang === 'go' && target.kind === 'method') {
93
+ // Go interfaces are satisfied structurally: a call through an interface this method's
94
+ // type implements may reach it.
95
+ const viaInterface = goInterfaceMethods(db, target);
96
+ if (viaInterface.length) {
97
+ family = new Set([...family, ...viaInterface]);
98
+ structural = new Set(viaInterface);
99
+ }
100
+ }
101
+ const edges = [];
102
+ const weak = [];
103
+ const targetIsMember = target.kind === 'method' || target.qname.includes('.');
104
+ // `get_or_create`, `maybeAutoCommit`: a name like that on an unknown receiver is worth listing as possible
105
+ // even from files that never mention the class.
106
+ const distinctive = target.name.length >= 8 && /[a-z0-9][A-Z_]|_[a-z]/.test(target.name);
107
+ // `@property def count` / `get count()`: read as an attribute, so `.count(...)` calls elsewhere are something else.
108
+ const propertyLike = /^@(cached_)?property\b|^(public |private |protected |static |override )*get \w/.test(target.signature);
109
+ const callerCache = new Map();
110
+ for (const row of rows) {
111
+ let caller = null;
112
+ if (row.caller_id !== null) {
113
+ if (!callerCache.has(row.caller_id))
114
+ callerCache.set(row.caller_id, db.symbolById(row.caller_id));
115
+ caller = callerCache.get(row.caller_id) ?? null;
116
+ }
117
+ const site = {
118
+ fileId: row.file_id, path: row.path, lang: row.lang, receiver: row.receiver, rtype: row.rtype, via: row.via,
119
+ argc: row.argc, caller,
120
+ };
121
+ // A call with another number of arguments is a call of another overload (or of something else).
122
+ if (!arityTargets.some(t => resolver.arityFits(t, site)))
123
+ continue;
124
+ // A symbol "calling itself" by name from inside its own definition line is recursion: keep it.
125
+ const res = resolver.resolve(target.name, site);
126
+ if (res.candidates.length === 0) {
127
+ // Nothing could be attributed. If the receiver's type is simply unknown,
128
+ // keep the site as a possible caller instead of silently dropping it.
129
+ if (targetIsMember && !propertyLike && weak.length < MAX_WEAK && (distinctive || resolver.canSee(row.file_id, target)) && resolver.isOpaqueReceiver(site) && langFamily(row.lang) === langFamily(target.lang) && !(isTestPath(target.path) && !isTestPath(row.path))) {
130
+ weak.push({ caller, path: row.path, line: row.line, count: row.count, certain: false, weak: true });
131
+ }
132
+ continue;
133
+ }
134
+ const top = res.candidates[0];
135
+ let certain;
136
+ if (family.has(top.sym.id)) {
137
+ // Resolved to the target or to its interface/override/overload twin.
138
+ certain = res.confident;
139
+ // Any type with these methods satisfies a Go interface: a call through it may reach the
140
+ // target, but equally one of the other implementers.
141
+ if (structural && structural.has(top.sym.id))
142
+ certain = false;
143
+ // Bound to an override in a subclass: that call does not run the target.
144
+ if (top.sym.id !== target.id && resolver.overriddenBelow(target, top.sym)) {
145
+ if (certain)
146
+ continue;
147
+ }
148
+ // Bound to a supertype's declaration, but on a receiver that cannot be the target's class
149
+ // (a sibling subclass, or an explicit super call).
150
+ if (certain && top.sym.id !== target.id && !resolver.reaches(target, top.sym, site))
151
+ continue;
152
+ }
153
+ else {
154
+ const mine = res.candidates.find(c => family.has(c.sym.id));
155
+ // Keep near-ties as uncertain edges; drop sites that clearly mean another definition.
156
+ if (!mine || top.score - mine.score > 10)
157
+ continue;
158
+ // The site binds to another copy of this definition (a vendored fork) that its file can see better.
159
+ if (top.sym.qname === target.qname && top.sym.kind === target.kind && top.sym.file_id !== target.file_id &&
160
+ top.score - mine.score >= 4)
161
+ continue;
162
+ certain = false;
163
+ }
164
+ edges.push({ caller, path: row.path, line: row.line, count: row.count, certain });
165
+ }
166
+ // Call sites that use a local alias of the target: `import { X as Y }`, default imports, require bindings.
167
+ for (const use of resolver.aliasesOf(target)) {
168
+ const aliasId = db.nameId(use.alias, false);
169
+ if (aliasId === undefined)
170
+ continue;
171
+ const aliasRows = db.all(`SELECT c.file_id, c.caller_id, c.line, c.receiver, c.rtype, c.via, c.count, f.path, f.lang
172
+ FROM calls c JOIN files f ON f.id = c.file_id
173
+ WHERE c.name_id = ? AND c.file_id = ? AND c.receiver IS NULL`, aliasId, use.fileId);
174
+ for (const row of aliasRows) {
175
+ const caller = row.caller_id !== null ? db.symbolById(row.caller_id) ?? null : null;
176
+ edges.push({ caller, path: row.path, line: row.line, count: row.count, certain: true });
177
+ }
178
+ }
179
+ // Unattributed sites only matter when they are not already explained.
180
+ const known = new Set(edges.map(e => e.path + ':' + e.line));
181
+ for (const w of weak)
182
+ if (!known.has(w.path + ':' + w.line))
183
+ edges.push(w);
184
+ // Production code first, then resolved before name-only, then by location.
185
+ edges.sort((a, b) => Number(isTestPath(a.path)) - Number(isTestPath(b.path)) ||
186
+ Number(b.certain) - Number(a.certain) ||
187
+ Number(a.weak ?? false) - Number(b.weak ?? false) ||
188
+ a.path.localeCompare(b.path) ||
189
+ a.line - b.line);
190
+ return edges;
191
+ }
192
+ /** What does `sym` call? For containers, the calls of all direct members. */
193
+ export function calleesOf(db, resolver, sym) {
194
+ const ids = bodyIds(db, sym);
195
+ const byName = new Map();
196
+ for (const id of ids) {
197
+ const caller = id === sym.id ? sym : db.symbolById(id) ?? sym;
198
+ const rows = db.all(`SELECT n.name, c.line, c.receiver, c.rtype, c.via, c.argc, c.count
199
+ FROM calls c JOIN names n ON n.id = c.name_id
200
+ WHERE c.caller_id = ? ORDER BY c.line`, id);
201
+ for (const row of rows) {
202
+ const res = resolver.resolve(row.name, {
203
+ fileId: sym.file_id, path: sym.path, lang: sym.lang, receiver: row.receiver, rtype: row.rtype, via: row.via,
204
+ argc: row.argc, caller,
205
+ });
206
+ const first = res.candidates[0];
207
+ // Follow the call to the implementation, not to a prototype / in-class declaration.
208
+ const top = first ? { ...first, sym: representative(db, resolver, first.sym) } : undefined;
209
+ const key = row.name + '\0' + (top ? top.sym.id : '');
210
+ const existing = byName.get(key);
211
+ if (existing) {
212
+ existing.count += row.count;
213
+ continue;
214
+ }
215
+ byName.set(key, {
216
+ name: row.name,
217
+ line: row.line,
218
+ count: row.count,
219
+ receiver: row.receiver,
220
+ target: top ? top.sym : null,
221
+ certain: res.confident,
222
+ alternatives: top
223
+ ? Math.max(0, res.candidates.length - 1)
224
+ // Unresolved, but the project does define methods with this name: not "external".
225
+ : resolver.isOpaqueReceiver({
226
+ fileId: sym.file_id, path: sym.path, lang: sym.lang, receiver: row.receiver, rtype: row.rtype, via: row.via, caller,
227
+ })
228
+ ? resolver.candidates(row.name).filter(c => langFamily(c.lang) === langFamily(sym.lang)).length
229
+ : 0,
230
+ });
231
+ }
232
+ }
233
+ return [...byName.values()].sort((a, b) => a.line - b.line);
234
+ }
235
+ export function groupCallers(edges) {
236
+ const groups = new Map();
237
+ for (const e of edges) {
238
+ const key = e.caller ? 's' + e.caller.id : 'f' + e.path;
239
+ const g = groups.get(key);
240
+ if (g) {
241
+ g.sites += e.count;
242
+ if (e.certain)
243
+ g.certain = true;
244
+ if (!e.weak)
245
+ g.weak = false;
246
+ if (e.line < g.line)
247
+ g.line = e.line;
248
+ }
249
+ else {
250
+ groups.set(key, {
251
+ caller: e.caller, path: e.path, line: e.line, sites: e.count, certain: e.certain, weak: e.weak ?? false,
252
+ });
253
+ }
254
+ }
255
+ return [...groups.values()];
256
+ }
257
+ /**
258
+ * Call sites of a name that has no definition in the project (an external
259
+ * library function, a dynamically defined method).
260
+ */
261
+ export function externalCallSites(db, name, limit = 2000) {
262
+ const nameId = db.nameId(name, false);
263
+ if (nameId === undefined)
264
+ return [];
265
+ const rows = db.all(`SELECT c.file_id, c.caller_id, c.line, c.receiver, c.rtype, c.via, c.count, f.path, f.lang
266
+ FROM calls c JOIN files f ON f.id = c.file_id WHERE c.name_id = ? LIMIT ?`, nameId, limit);
267
+ const edges = rows.map(row => ({
268
+ caller: row.caller_id !== null ? db.symbolById(row.caller_id) ?? null : null,
269
+ path: row.path, line: row.line, count: row.count, certain: false,
270
+ }));
271
+ edges.sort((a, b) => Number(isTestPath(a.path)) - Number(isTestPath(b.path)) || a.path.localeCompare(b.path) || a.line - b.line);
272
+ return edges;
273
+ }
274
+ /** Symbols with more direct edges than this are not expanded below the first level. */
275
+ const HUB_LIMIT = 30;
276
+ /**
277
+ * Breadth-first expansion of callers or callees, bounded by a total node
278
+ * budget so the output stays small. Hubs (symbols with dozens of callers)
279
+ * are reported but not expanded beyond the first level: their fan-out would
280
+ * drown the rest of the tree.
281
+ */
282
+ export function buildGraph(db, resolver, root, direction, opts) {
283
+ const rootNode = { sym: root, path: root.path, line: root.start_line, certain: true, sites: 1, children: [] };
284
+ const expanded = new Set([root.id]);
285
+ let frontier = [rootNode];
286
+ let remaining = opts.budget;
287
+ for (let level = 0; level < opts.depth && frontier.length && remaining > 0; level++) {
288
+ const next = [];
289
+ // Split the remaining budget fairly across this level's nodes.
290
+ const perNode = level === 0 ? remaining : Math.max(3, Math.ceil(remaining / frontier.length));
291
+ for (const node of frontier) {
292
+ if (!node.sym || remaining <= 0)
293
+ continue;
294
+ let children;
295
+ if (direction === 'callers') {
296
+ children = groupCallers(callersOf(db, resolver, node.sym, { pathContains: opts.pathContains, overloads: level === 0 ? opts.overloads : undefined }, level === 0 ? opts.stats : undefined))
297
+ .filter(g => (opts.includeTests || !isTestPath(g.path)) && (!opts.path || opts.path(g.path)))
298
+ .map(g => ({
299
+ sym: g.caller, path: g.path, line: g.line, certain: g.certain, sites: g.sites, weak: g.weak,
300
+ label: g.caller ? undefined : '<file level>', children: [],
301
+ }));
302
+ }
303
+ else {
304
+ children = calleesOf(db, resolver, node.sym)
305
+ .filter(e => e.target !== null && (opts.includeTests || !isTestPath(e.target.path)))
306
+ .map(e => ({ sym: e.target, path: e.target.path, line: e.line, certain: e.certain, sites: e.count, children: [] }));
307
+ }
308
+ if (level > 0 && children.length > HUB_LIMIT) {
309
+ node.hub = children.length;
310
+ continue;
311
+ }
312
+ const take = Math.min(children.length, perNode, remaining);
313
+ node.omitted = children.length - take;
314
+ node.children = children.slice(0, take);
315
+ remaining -= take;
316
+ for (const child of node.children) {
317
+ // Only confirmed edges are worth following further.
318
+ if (!child.sym || child.weak || (direction === 'callers' && !child.certain))
319
+ continue;
320
+ if (expanded.has(child.sym.id)) {
321
+ child.repeated = true;
322
+ continue;
323
+ }
324
+ expanded.add(child.sym.id);
325
+ next.push(child);
326
+ }
327
+ }
328
+ frontier = next;
329
+ }
330
+ return rootNode;
331
+ }
332
+ //# sourceMappingURL=callgraph.js.map