clast-ai 0.1.2 → 0.2.1

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 (294) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +11 -9
  3. package/README.md +468 -398
  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 +266 -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/migrate.d.ts +35 -0
  59. package/dist/migrate.js +194 -0
  60. package/dist/migrate.js.map +1 -0
  61. package/dist/parser/extractor.d.ts +21 -16
  62. package/dist/parser/extractor.js +700 -228
  63. package/dist/parser/extractor.js.map +1 -1
  64. package/dist/parser/index.d.ts +17 -21
  65. package/dist/parser/index.js +115 -171
  66. package/dist/parser/index.js.map +1 -1
  67. package/dist/parser/langs/c.d.ts +3 -0
  68. package/dist/parser/langs/c.js +879 -0
  69. package/dist/parser/langs/c.js.map +1 -0
  70. package/dist/parser/langs/csharp.d.ts +2 -0
  71. package/dist/parser/langs/csharp.js +548 -0
  72. package/dist/parser/langs/csharp.js.map +1 -0
  73. package/dist/parser/langs/dart.d.ts +2 -0
  74. package/dist/parser/langs/dart.js +893 -0
  75. package/dist/parser/langs/dart.js.map +1 -0
  76. package/dist/parser/langs/go.d.ts +2 -0
  77. package/dist/parser/langs/go.js +455 -0
  78. package/dist/parser/langs/go.js.map +1 -0
  79. package/dist/parser/langs/java.d.ts +2 -0
  80. package/dist/parser/langs/java.js +390 -0
  81. package/dist/parser/langs/java.js.map +1 -0
  82. package/dist/parser/langs/php.d.ts +2 -0
  83. package/dist/parser/langs/php.js +486 -0
  84. package/dist/parser/langs/php.js.map +1 -0
  85. package/dist/parser/langs/python.d.ts +2 -0
  86. package/dist/parser/langs/python.js +216 -0
  87. package/dist/parser/langs/python.js.map +1 -0
  88. package/dist/parser/langs/ruby.d.ts +2 -0
  89. package/dist/parser/langs/ruby.js +602 -0
  90. package/dist/parser/langs/ruby.js.map +1 -0
  91. package/dist/parser/langs/rust.d.ts +2 -0
  92. package/dist/parser/langs/rust.js +931 -0
  93. package/dist/parser/langs/rust.js.map +1 -0
  94. package/dist/parser/langs/swift.d.ts +2 -0
  95. package/dist/parser/langs/swift.js +616 -0
  96. package/dist/parser/langs/swift.js.map +1 -0
  97. package/dist/parser/langs/typescript.d.ts +4 -0
  98. package/dist/parser/langs/typescript.js +535 -0
  99. package/dist/parser/langs/typescript.js.map +1 -0
  100. package/dist/parser/registry.d.ts +16 -0
  101. package/dist/parser/registry.js +87 -0
  102. package/dist/parser/registry.js.map +1 -0
  103. package/dist/parser/returns.d.ts +9 -0
  104. package/dist/parser/returns.js +152 -0
  105. package/dist/parser/returns.js.map +1 -0
  106. package/dist/parser/spec.d.ts +165 -0
  107. package/dist/parser/spec.js +188 -0
  108. package/dist/parser/spec.js.map +1 -0
  109. package/dist/query/arity.d.ts +27 -0
  110. package/dist/query/arity.js +187 -0
  111. package/dist/query/arity.js.map +1 -0
  112. package/dist/query/callgraph.d.ts +113 -0
  113. package/dist/query/callgraph.js +332 -0
  114. package/dist/query/callgraph.js.map +1 -0
  115. package/dist/query/format.d.ts +36 -0
  116. package/dist/query/format.js +136 -0
  117. package/dist/query/format.js.map +1 -0
  118. package/dist/query/hierarchy.d.ts +44 -0
  119. package/dist/query/hierarchy.js +441 -0
  120. package/dist/query/hierarchy.js.map +1 -0
  121. package/dist/query/resolve.d.ts +265 -0
  122. package/dist/query/resolve.js +1957 -0
  123. package/dist/query/resolve.js.map +1 -0
  124. package/dist/query/search.d.ts +35 -0
  125. package/dist/query/search.js +379 -0
  126. package/dist/query/search.js.map +1 -0
  127. package/dist/query/words.d.ts +30 -0
  128. package/dist/query/words.js +201 -0
  129. package/dist/query/words.js.map +1 -0
  130. package/dist/server/index.d.ts +1 -3
  131. package/dist/server/index.js +313 -73
  132. package/dist/server/index.js.map +1 -1
  133. package/dist/tools/context.d.ts +35 -0
  134. package/dist/tools/context.js +79 -0
  135. package/dist/tools/context.js.map +1 -0
  136. package/dist/tools/explore.d.ts +51 -0
  137. package/dist/tools/explore.js +779 -0
  138. package/dist/tools/explore.js.map +1 -0
  139. package/dist/tools/impact.d.ts +21 -0
  140. package/dist/tools/impact.js +743 -0
  141. package/dist/tools/impact.js.map +1 -0
  142. package/dist/tools/search.d.ts +59 -0
  143. package/dist/tools/search.js +843 -0
  144. package/dist/tools/search.js.map +1 -0
  145. package/dist/tools/stats.d.ts +26 -0
  146. package/dist/tools/stats.js +604 -0
  147. package/dist/tools/stats.js.map +1 -0
  148. package/dist/types.d.ts +95 -40
  149. package/dist/types.js +9 -1
  150. package/dist/types.js.map +1 -1
  151. package/dist/version.d.ts +2 -0
  152. package/dist/version.js +3 -0
  153. package/dist/version.js.map +1 -0
  154. package/dist/watcher/index.d.ts +18 -16
  155. package/dist/watcher/index.js +94 -77
  156. package/dist/watcher/index.js.map +1 -1
  157. package/package.json +21 -24
  158. package/plugin/server.mjs +72 -0
  159. package/skills/audit/SKILL.md +37 -0
  160. package/skills/deadcode/SKILL.md +33 -0
  161. package/skills/explore/SKILL.md +54 -0
  162. package/skills/impact/SKILL.md +24 -0
  163. package/skills/status/SKILL.md +11 -0
  164. package/src/cli.ts +265 -0
  165. package/src/config/defaults.ts +50 -32
  166. package/src/config/index.ts +23 -14
  167. package/src/db/index.ts +540 -43
  168. package/src/engine.ts +30 -0
  169. package/src/indexer/imports.ts +241 -0
  170. package/src/indexer/index.ts +369 -0
  171. package/src/indexer/process.ts +63 -0
  172. package/src/indexer/scan.ts +226 -0
  173. package/src/indexer/worker.ts +24 -0
  174. package/src/llm/comment-generator.ts +52 -103
  175. package/src/llm/index.ts +80 -80
  176. package/src/llm/prompts.ts +95 -95
  177. package/src/migrate.ts +213 -0
  178. package/src/parser/extractor.ts +643 -256
  179. package/src/parser/index.ts +105 -205
  180. package/src/parser/langs/c.ts +860 -0
  181. package/src/parser/langs/csharp.ts +519 -0
  182. package/src/parser/langs/dart.ts +820 -0
  183. package/src/parser/langs/go.ts +437 -0
  184. package/src/parser/langs/java.ts +361 -0
  185. package/src/parser/langs/php.ts +450 -0
  186. package/src/parser/langs/python.ts +206 -0
  187. package/src/parser/langs/ruby.ts +554 -0
  188. package/src/parser/langs/rust.ts +904 -0
  189. package/src/parser/langs/swift.ts +590 -0
  190. package/src/parser/langs/typescript.ts +499 -0
  191. package/src/parser/registry.ts +88 -0
  192. package/src/parser/returns.ts +137 -0
  193. package/src/parser/spec.ts +309 -0
  194. package/src/query/arity.ts +173 -0
  195. package/src/query/callgraph.ts +473 -0
  196. package/src/query/format.ts +136 -0
  197. package/src/query/hierarchy.ts +447 -0
  198. package/src/query/resolve.ts +1972 -0
  199. package/src/query/search.ts +430 -0
  200. package/src/query/words.ts +183 -0
  201. package/src/server/index.ts +390 -79
  202. package/src/tools/context.ts +82 -0
  203. package/src/tools/explore.ts +860 -0
  204. package/src/tools/impact.ts +748 -0
  205. package/src/tools/search.ts +886 -0
  206. package/src/tools/stats.ts +633 -0
  207. package/src/types.ts +113 -45
  208. package/src/version.ts +2 -0
  209. package/src/watcher/index.ts +82 -88
  210. package/tsconfig.json +6 -3
  211. package/.mcp.json +0 -8
  212. package/commands/clast-status.md +0 -9
  213. package/dist/config/defaults.d.ts.map +0 -1
  214. package/dist/config/index.d.ts.map +0 -1
  215. package/dist/db/index.d.ts.map +0 -1
  216. package/dist/db/queries.d.ts +0 -44
  217. package/dist/db/queries.d.ts.map +0 -1
  218. package/dist/db/queries.js +0 -251
  219. package/dist/db/queries.js.map +0 -1
  220. package/dist/db/schema.d.ts +0 -3
  221. package/dist/db/schema.d.ts.map +0 -1
  222. package/dist/db/schema.js +0 -94
  223. package/dist/db/schema.js.map +0 -1
  224. package/dist/db/types.d.ts +0 -44
  225. package/dist/db/types.d.ts.map +0 -1
  226. package/dist/db/types.js +0 -2
  227. package/dist/db/types.js.map +0 -1
  228. package/dist/llm/comment-generator.d.ts.map +0 -1
  229. package/dist/llm/index.d.ts.map +0 -1
  230. package/dist/llm/prompts.d.ts.map +0 -1
  231. package/dist/parser/call-resolver.d.ts +0 -15
  232. package/dist/parser/call-resolver.d.ts.map +0 -1
  233. package/dist/parser/call-resolver.js +0 -16
  234. package/dist/parser/call-resolver.js.map +0 -1
  235. package/dist/parser/extractor.d.ts.map +0 -1
  236. package/dist/parser/index.d.ts.map +0 -1
  237. package/dist/parser/languages.d.ts +0 -11
  238. package/dist/parser/languages.d.ts.map +0 -1
  239. package/dist/parser/languages.js +0 -74
  240. package/dist/parser/languages.js.map +0 -1
  241. package/dist/server/index.d.ts.map +0 -1
  242. package/dist/server/register-tools.d.ts +0 -7
  243. package/dist/server/register-tools.d.ts.map +0 -1
  244. package/dist/server/register-tools.js +0 -17
  245. package/dist/server/register-tools.js.map +0 -1
  246. package/dist/server/tools/clast-call-graph.d.ts +0 -4
  247. package/dist/server/tools/clast-call-graph.d.ts.map +0 -1
  248. package/dist/server/tools/clast-call-graph.js +0 -70
  249. package/dist/server/tools/clast-call-graph.js.map +0 -1
  250. package/dist/server/tools/clast-comment.d.ts +0 -5
  251. package/dist/server/tools/clast-comment.d.ts.map +0 -1
  252. package/dist/server/tools/clast-comment.js +0 -64
  253. package/dist/server/tools/clast-comment.js.map +0 -1
  254. package/dist/server/tools/clast-file-summary.d.ts +0 -4
  255. package/dist/server/tools/clast-file-summary.d.ts.map +0 -1
  256. package/dist/server/tools/clast-file-summary.js +0 -42
  257. package/dist/server/tools/clast-file-summary.js.map +0 -1
  258. package/dist/server/tools/clast-get-context.d.ts +0 -4
  259. package/dist/server/tools/clast-get-context.d.ts.map +0 -1
  260. package/dist/server/tools/clast-get-context.js +0 -80
  261. package/dist/server/tools/clast-get-context.js.map +0 -1
  262. package/dist/server/tools/clast-reindex.d.ts +0 -5
  263. package/dist/server/tools/clast-reindex.d.ts.map +0 -1
  264. package/dist/server/tools/clast-reindex.js +0 -36
  265. package/dist/server/tools/clast-reindex.js.map +0 -1
  266. package/dist/server/tools/clast-search.d.ts +0 -4
  267. package/dist/server/tools/clast-search.d.ts.map +0 -1
  268. package/dist/server/tools/clast-search.js +0 -29
  269. package/dist/server/tools/clast-search.js.map +0 -1
  270. package/dist/server/tools/clast-status.d.ts +0 -5
  271. package/dist/server/tools/clast-status.d.ts.map +0 -1
  272. package/dist/server/tools/clast-status.js +0 -23
  273. package/dist/server/tools/clast-status.js.map +0 -1
  274. package/dist/types.d.ts.map +0 -1
  275. package/dist/watcher/gitignore.d.ts +0 -9
  276. package/dist/watcher/gitignore.d.ts.map +0 -1
  277. package/dist/watcher/gitignore.js +0 -38
  278. package/dist/watcher/gitignore.js.map +0 -1
  279. package/dist/watcher/index.d.ts.map +0 -1
  280. package/skills/clast-explore/SKILL.md +0 -41
  281. package/src/db/queries.ts +0 -332
  282. package/src/db/schema.ts +0 -106
  283. package/src/db/types.ts +0 -47
  284. package/src/parser/call-resolver.ts +0 -17
  285. package/src/parser/languages.ts +0 -86
  286. package/src/server/register-tools.ts +0 -30
  287. package/src/server/tools/clast-call-graph.ts +0 -82
  288. package/src/server/tools/clast-comment.ts +0 -76
  289. package/src/server/tools/clast-file-summary.ts +0 -59
  290. package/src/server/tools/clast-get-context.ts +0 -97
  291. package/src/server/tools/clast-reindex.ts +0 -52
  292. package/src/server/tools/clast-search.ts +0 -38
  293. package/src/server/tools/clast-status.ts +0 -37
  294. package/src/watcher/gitignore.ts +0 -42
@@ -0,0 +1,879 @@
1
+ import { cleanComment } from '../extractor.js';
2
+ import { childOfType, field, isConstName, simpleReceiver, stripQuotes } from '../spec.js';
3
+ const MAX_SIG_SOURCE = 400;
4
+ const MAX_TYPE_TEXT = 100;
5
+ /** struct/union/enum/class specifier node type -> symbol kind. */
6
+ const CONTAINER_KIND = {
7
+ struct_specifier: 'struct',
8
+ union_specifier: 'struct',
9
+ class_specifier: 'class',
10
+ enum_specifier: 'enum',
11
+ };
12
+ /** Name of a type-ish node used as a scope or base: `Foo`, `Foo<T>`, `a::Foo`. */
13
+ function typeName(node) {
14
+ let cur = node;
15
+ for (let i = 0; cur && i < 12; i++) {
16
+ switch (cur.type) {
17
+ case 'qualified_identifier':
18
+ cur = field(cur, 'name');
19
+ break;
20
+ case 'template_type':
21
+ case 'template_function':
22
+ case 'template_method':
23
+ case 'dependent_name':
24
+ case 'dependent_type':
25
+ cur = field(cur, 'name') ?? cur.lastNamedChild;
26
+ break;
27
+ case 'type_identifier':
28
+ case 'identifier':
29
+ case 'namespace_identifier':
30
+ case 'field_identifier':
31
+ return cur.text;
32
+ default:
33
+ return '';
34
+ }
35
+ }
36
+ return '';
37
+ }
38
+ function operatorName(raw) {
39
+ const rest = raw.replace(/^operator/, '').replace(/\s+/g, ' ').trim();
40
+ // `operator new`, `operator""_kb` keep a separator; symbolic operators do not.
41
+ return /^[A-Za-z_]/.test(rest) ? 'operator ' + rest : 'operator' + rest.replace(/ /g, '');
42
+ }
43
+ /** Resolve the (possibly qualified) name node at the bottom of a declarator. */
44
+ function leafName(leaf, fn) {
45
+ const scopes = [];
46
+ let cur = leaf;
47
+ for (let i = 0; cur && cur.type === 'qualified_identifier' && i < 12; i++) {
48
+ const scope = field(cur, 'scope');
49
+ // `EXPORT_MACRO Status Foo()` is parsed as type EXPORT_MACRO + a qualified
50
+ // identifier `Status Foo` with a zero-width (missing) `::`; that scope is
51
+ // really the return type.
52
+ const sep = scope ? cur.child(1) : null;
53
+ if (scope && sep && sep.endIndex > sep.startIndex) {
54
+ const s = typeName(scope);
55
+ if (s)
56
+ scopes.push(s);
57
+ }
58
+ cur = field(cur, 'name');
59
+ }
60
+ if (!cur)
61
+ return null;
62
+ let name = '';
63
+ let isFn = fn !== null;
64
+ switch (cur.type) {
65
+ case 'identifier':
66
+ case 'field_identifier':
67
+ case 'type_identifier':
68
+ name = cur.text;
69
+ break;
70
+ case 'destructor_name':
71
+ name = cur.text.replace(/\s+/g, '');
72
+ break;
73
+ case 'operator_name':
74
+ name = operatorName(cur.text);
75
+ break;
76
+ case 'operator_cast': {
77
+ const t = field(cur, 'type');
78
+ if (!t)
79
+ return null;
80
+ name = 'operator ' + t.text.replace(/\s+/g, ' ');
81
+ isFn = true;
82
+ break;
83
+ }
84
+ case 'template_function':
85
+ case 'template_method':
86
+ case 'template_type':
87
+ name = typeName(cur);
88
+ break;
89
+ default:
90
+ return null;
91
+ }
92
+ if (!name || name.length > 100)
93
+ return null;
94
+ if (!name.startsWith('operator') && !/^~?[A-Za-z_$][\w$]*$/.test(name))
95
+ return null;
96
+ return { name, qualifier: scopes.length ? scopes.join('::') : null, isFn, fn };
97
+ }
98
+ /**
99
+ * Unwrap pointer / reference / array / parenthesized / init / function
100
+ * declarators down to the declared name.
101
+ */
102
+ function declared(d) {
103
+ let cur = d;
104
+ let fn = null;
105
+ let paren = false;
106
+ for (let i = 0; cur && i < 24; i++) {
107
+ switch (cur.type) {
108
+ case 'function_declarator':
109
+ fn = cur;
110
+ paren = false;
111
+ cur = field(cur, 'declarator');
112
+ break;
113
+ case 'parenthesized_declarator':
114
+ paren = true;
115
+ cur = cur.lastNamedChild;
116
+ break;
117
+ case 'pointer_declarator':
118
+ case 'reference_declarator':
119
+ // `(*name)(args)` is a pointer to function, not a function.
120
+ if (paren)
121
+ fn = null;
122
+ cur = field(cur, 'declarator') ?? cur.lastNamedChild;
123
+ break;
124
+ case 'array_declarator':
125
+ case 'init_declarator':
126
+ case 'attributed_declarator':
127
+ cur = field(cur, 'declarator') ?? cur.firstNamedChild;
128
+ break;
129
+ default:
130
+ return leafName(cur, fn);
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+ /**
136
+ * `class EXPORT_MACRO Name { ... };` is parsed by tree-sitter as a function
137
+ * definition: return type `class EXPORT_MACRO`, declarator `Name`, and the
138
+ * class body as a block of statements. Returns the specifier node if so.
139
+ */
140
+ function pseudoClass(fnDef) {
141
+ const d = field(fnDef, 'declarator');
142
+ if (!d || (d.type !== 'identifier' && d.type !== 'type_identifier'))
143
+ return null;
144
+ const type = field(fnDef, 'type');
145
+ if (!type || (type.type !== 'class_specifier' && type.type !== 'struct_specifier'))
146
+ return null;
147
+ return field(type, 'body') ? null : type;
148
+ }
149
+ function where(node) {
150
+ let anon = false;
151
+ for (let p = node.parent; p; p = p.parent) {
152
+ const t = p.type;
153
+ if (t === 'compound_statement') {
154
+ const owner = p.parent;
155
+ if (owner && owner.type === 'function_definition' && pseudoClass(owner))
156
+ continue;
157
+ return { local: true, anon };
158
+ }
159
+ if (t === 'namespace_definition' && !field(p, 'name'))
160
+ anon = true;
161
+ }
162
+ return { local: false, anon };
163
+ }
164
+ /** Widen to enclosing template_declaration(s) so docs and `template<...>` are picked up. */
165
+ function outer(node) {
166
+ let cur = node;
167
+ for (let p = cur.parent; p && p.type === 'template_declaration'; p = p.parent)
168
+ cur = p;
169
+ return cur;
170
+ }
171
+ function hasSpecifier(node, type, re) {
172
+ for (const c of node.namedChildren) {
173
+ if (c && c.type === type && re.test(c.text))
174
+ return true;
175
+ }
176
+ return false;
177
+ }
178
+ const isStatic = (node) => hasSpecifier(node, 'storage_class_specifier', /^static$/);
179
+ const isExtern = (node) => hasSpecifier(node, 'storage_class_specifier', /^extern$/);
180
+ const isConst = (node) => hasSpecifier(node, 'type_qualifier', /^(const|constexpr)$/);
181
+ function inType(ctx) {
182
+ const k = ctx.parentKind();
183
+ return k === 'class' || k === 'struct';
184
+ }
185
+ /** Source from the start of `range` up to `end`, without trailing `;`, `{` or `=`. */
186
+ function sigText(ctx, range, end, stripAssign = false) {
187
+ const start = range.startIndex;
188
+ const s = ctx.src.slice(start, Math.min(end, start + MAX_SIG_SOURCE));
189
+ return s.replace(stripAssign ? /[\s;{=]+$/ : /[\s;{]+$/, '');
190
+ }
191
+ /**
192
+ * Signature for a declaration whose type is an inline struct/enum body:
193
+ * `struct cfg { ... } name` instead of the whole body.
194
+ */
195
+ function declSig(ctx, node, range, name, value) {
196
+ const type = field(node, 'type');
197
+ const body = type && CONTAINER_KIND[type.type] ? field(type, 'body') : null;
198
+ if (body)
199
+ return sigText(ctx, range, body.startIndex) + ' { ... } ' + name;
200
+ return sigText(ctx, range, value ? value.startIndex : range.endIndex, value !== null);
201
+ }
202
+ /**
203
+ * Preprocessor nodes end at column 0 of the following line, which makes the
204
+ * generic doc lookup mistake a comment on that line for a trailing comment of
205
+ * the directive. Recover the doc comment in that case:
206
+ *
207
+ * #include "x.h"
208
+ * // documented
209
+ * int f(void);
210
+ */
211
+ function docAfterPreproc(range) {
212
+ let c = range.previousSibling;
213
+ if (!c || c.type !== 'comment')
214
+ return undefined;
215
+ const parts = [];
216
+ let nextRow = range.startPosition.row;
217
+ while (c && c.type === 'comment' && c.endPosition.row >= nextRow - 1 && parts.length < 60) {
218
+ parts.unshift(c.text);
219
+ nextRow = c.startPosition.row;
220
+ c = c.previousSibling;
221
+ }
222
+ if (!c || !c.type.startsWith('preproc_') || c.endPosition.column !== 0 || c.endPosition.row !== nextRow)
223
+ return undefined;
224
+ return parts.map(cleanComment).join('\n').trim() || undefined;
225
+ }
226
+ function define(ctx, node, opts) {
227
+ const doc = docAfterPreproc(opts.range ?? node);
228
+ return ctx.def(node, doc ? { ...opts, doc } : opts);
229
+ }
230
+ // ---------------------------------------------------------------------------
231
+ // Receiver type hints
232
+ // ---------------------------------------------------------------------------
233
+ /**
234
+ * Raw text of a declared type worth a hint, or null for primitives, `auto`,
235
+ * decltype and anything large. Normalisation is left to ctx.typeHint().
236
+ */
237
+ function typeText(type) {
238
+ if (!type)
239
+ return null;
240
+ switch (type.type) {
241
+ case 'type_identifier':
242
+ return type.text;
243
+ case 'qualified_identifier':
244
+ case 'template_type':
245
+ return type.endIndex - type.startIndex <= MAX_TYPE_TEXT ? type.text : null;
246
+ case 'struct_specifier':
247
+ case 'class_specifier':
248
+ case 'union_specifier': {
249
+ const name = field(type, 'name'); // `struct Foo *p`; never the text of a body
250
+ return name && name.type === 'type_identifier' ? name.text : null;
251
+ }
252
+ default:
253
+ return null;
254
+ }
255
+ }
256
+ /** Templates whose first type argument is the type of the result. */
257
+ const TYPE_MAKERS = new Set([
258
+ 'make_unique', 'make_shared', 'static_cast', 'dynamic_cast', 'reinterpret_cast', 'const_cast',
259
+ 'static_pointer_cast', 'dynamic_pointer_cast', 'down_cast', 'MakeUnique', 'MakeShared',
260
+ ]);
261
+ /** Type of an `auto` variable from its initializer: `new Foo(...)`, `std::make_unique<Foo>(...)`, `static_cast<Foo*>(p)`. */
262
+ function inferredType(value) {
263
+ if (!value)
264
+ return null;
265
+ if (value.type === 'new_expression')
266
+ return typeText(field(value, 'type'));
267
+ if (value.type !== 'call_expression')
268
+ return null;
269
+ let fn = field(value, 'function');
270
+ for (let i = 0; fn && fn.type === 'qualified_identifier' && i < 4; i++)
271
+ fn = field(fn, 'name');
272
+ if (!fn || fn.type !== 'template_function')
273
+ return null;
274
+ const name = field(fn, 'name');
275
+ if (!name || !TYPE_MAKERS.has(name.text))
276
+ return null;
277
+ const arg = field(fn, 'arguments')?.firstNamedChild;
278
+ return arg && arg.endIndex - arg.startIndex <= MAX_TYPE_TEXT ? arg.text : null;
279
+ }
280
+ /** Name node of a plain / pointer / reference declarator; null for functions, arrays, bindings. */
281
+ function hintTarget(d) {
282
+ let cur = d;
283
+ for (let i = 0; cur && i < 6; i++) {
284
+ switch (cur.type) {
285
+ case 'identifier':
286
+ case 'field_identifier':
287
+ return cur;
288
+ case 'init_declarator':
289
+ case 'pointer_declarator':
290
+ cur = field(cur, 'declarator');
291
+ break;
292
+ case 'reference_declarator':
293
+ cur = field(cur, 'declarator') ?? cur.lastNamedChild;
294
+ break;
295
+ default:
296
+ return null;
297
+ }
298
+ }
299
+ return null;
300
+ }
301
+ /** Hints for `Foo x;`, `Foo* p = ...`, `auto p = new Foo(...)`, `MemTable* mem_;` (declaration / field_declaration). */
302
+ function hintDeclaration(node, ctx) {
303
+ const type = field(node, 'type');
304
+ if (!type)
305
+ return;
306
+ const auto = type.type === 'placeholder_type_specifier';
307
+ const text = auto ? null : typeText(type);
308
+ if (!auto && !text)
309
+ return;
310
+ for (const decl of node.childrenForFieldName('declarator')) {
311
+ if (!decl)
312
+ continue;
313
+ const name = hintTarget(decl);
314
+ if (!name)
315
+ continue;
316
+ if (!auto)
317
+ ctx.typeHint(name.text, text);
318
+ // `if (auto* p = new Foo)` keeps the value on the declaration itself.
319
+ else
320
+ ctx.typeHint(name.text, inferredType(field(decl.type === 'init_declarator' ? decl : node, 'value')));
321
+ }
322
+ }
323
+ /** True for parameters of a function definition or lambda (not of prototypes or function pointer types). */
324
+ function ownedByDefinition(param) {
325
+ let p = param.parent?.parent; // parameter_list > (abstract_)function_declarator
326
+ if (!p)
327
+ return false;
328
+ if (p.type === 'abstract_function_declarator')
329
+ return p.parent?.type === 'lambda_expression';
330
+ for (let i = 0; p && i < 8; i++, p = p.parent) {
331
+ if (p.type === 'function_definition')
332
+ return true;
333
+ if (!p.type.endsWith('_declarator'))
334
+ return false;
335
+ }
336
+ return false;
337
+ }
338
+ const parameter = (node, ctx) => {
339
+ const text = typeText(field(node, 'type'));
340
+ if (!text)
341
+ return;
342
+ const name = hintTarget(field(node, 'declarator'));
343
+ if (name && ownedByDefinition(node))
344
+ ctx.typeHint(name.text, text);
345
+ };
346
+ /** `for (const Item& it : items)` */
347
+ const forRange = (node, ctx) => {
348
+ const text = typeText(field(node, 'type'));
349
+ if (!text)
350
+ return;
351
+ const name = hintTarget(field(node, 'declarator'));
352
+ if (name)
353
+ ctx.typeHint(name.text, text);
354
+ };
355
+ // ---------------------------------------------------------------------------
356
+ // Visitors
357
+ // ---------------------------------------------------------------------------
358
+ /** `TEST(Suite, Name) { ... }` style macro-defined functions: name them by their arguments. */
359
+ function macroDefinedName(fn) {
360
+ const params = field(fn, 'parameters');
361
+ if (!params || params.namedChildCount === 0 || params.namedChildCount > 2)
362
+ return null;
363
+ const parts = [];
364
+ for (const p of params.namedChildren) {
365
+ if (!p || p.type !== 'parameter_declaration' || p.namedChildCount !== 1)
366
+ return null;
367
+ if (p.firstNamedChild?.type !== 'type_identifier')
368
+ return null;
369
+ parts.push(p.text);
370
+ }
371
+ return parts.length === 2 ? { name: parts[1], qualifier: parts[0] } : { name: parts[0], qualifier: null };
372
+ }
373
+ /**
374
+ * Attribute macros confuse the parser: in `void ATTR(malloc) je_malloc(size_t n)`
375
+ * it takes ATTR for the function. When the parsed name is a MACRO_NAME, prefer
376
+ * the identifier in front of the last parameter list of the signature.
377
+ */
378
+ function nameBehindMacros(name, sig) {
379
+ if (!isConstName(name))
380
+ return name;
381
+ const m = /([A-Za-z_]\w*)\s*\([^()]*\)[\s\w]*$/.exec(sig);
382
+ return m && !isConstName(m[1]) ? m[1] : name;
383
+ }
384
+ /**
385
+ * Annotation macros after a declarator, as in
386
+ * 'void Add(int n) LOCKS_EXCLUDED(mu_) { ... }' or 'int n_ GUARDED_BY(mu_);',
387
+ * are parsed as a second function named after the macro, following a
388
+ * declaration whose ';' is missing.
389
+ */
390
+ function isTrailingAnnotation(node, name) {
391
+ if (!isConstName(name) || field(node, 'type'))
392
+ return false;
393
+ const prev = node.previousSibling;
394
+ if (!prev || (prev.type !== 'field_declaration' && prev.type !== 'declaration'))
395
+ return false;
396
+ return missingSemicolon(prev);
397
+ }
398
+ function missingSemicolon(decl) {
399
+ const last = decl.lastChild;
400
+ return !!last && last.type === ';' && last.endIndex === last.startIndex;
401
+ }
402
+ /**
403
+ * 'MemTable* imm_ GUARDED_BY(mutex_);' with a non-primitive type is parsed as a
404
+ * function GUARDED_BY whose real name sits in an ERROR node in front of the
405
+ * function declarator. Returns that name node.
406
+ */
407
+ function annotatedField(decl) {
408
+ let cur = decl;
409
+ for (let i = 0; cur && i < 6; i++) {
410
+ const prev = cur.previousSibling;
411
+ if (prev && prev.type === 'ERROR') {
412
+ const only = prev.namedChildCount === 1 ? prev.firstNamedChild : null;
413
+ return only && only.type === 'identifier' ? only : null;
414
+ }
415
+ if (cur.type !== 'pointer_declarator' && cur.type !== 'reference_declarator')
416
+ return null;
417
+ cur = field(cur, 'declarator') ?? cur.lastNamedChild;
418
+ }
419
+ return null;
420
+ }
421
+ /** Signature of a declaration with parse errors: never run into a body. */
422
+ function cutAtBrace(sig) {
423
+ const b = sig.indexOf('{');
424
+ return b > 0 ? sig.slice(0, b).trimEnd() : sig;
425
+ }
426
+ /** See pseudoClass(): recover the class and (best effort) its bases. */
427
+ function macroClass(node, spec, ctx) {
428
+ const body = field(node, 'body');
429
+ if (!body || ctx.inCallable())
430
+ return;
431
+ // Text between 'class EXPORT_MACRO' and the body: 'Name [final] [: bases]'.
432
+ const header = ctx.src.slice(spec.endIndex, Math.min(body.startIndex, spec.endIndex + MAX_SIG_SOURCE));
433
+ const m = /^\s*([A-Za-z_]\w*)\s*(?:final\s*)?(?::(?!:)(.*))?$/s.exec(header);
434
+ if (!m)
435
+ return;
436
+ const w = where(node);
437
+ if (w.local)
438
+ return;
439
+ const range = outer(node);
440
+ const sym = define(ctx, node, {
441
+ name: m[1],
442
+ kind: CONTAINER_KIND[spec.type] ?? 'class',
443
+ range,
444
+ sig: sigText(ctx, range, body.startIndex),
445
+ exported: !w.anon,
446
+ });
447
+ if (!m[2])
448
+ return;
449
+ for (const base of m[2].replace(/<[^<>]*>/g, '').split(',')) {
450
+ const b = /([A-Za-z_]\w*)\s*$/.exec(base);
451
+ if (b)
452
+ ctx.inherit(sym, b[1], 'extends');
453
+ }
454
+ }
455
+ const functionDefinition = (node, ctx) => {
456
+ const spec = pseudoClass(node);
457
+ if (spec)
458
+ return macroClass(node, spec, ctx);
459
+ const d = declared(field(node, 'declarator'));
460
+ if (!d)
461
+ return;
462
+ if (!d.isFn) {
463
+ // `TEST_BEGIN(name) { ... }` at file level: type TEST_BEGIN, declarator `(name)`.
464
+ const type = field(node, 'type');
465
+ const paren = field(node, 'declarator')?.type === 'parenthesized_declarator';
466
+ if (!paren || type?.type !== 'type_identifier' || !isConstName(type.text) || !field(node, 'body'))
467
+ return;
468
+ }
469
+ if (isTrailingAnnotation(node, d.name))
470
+ return;
471
+ const w = where(node);
472
+ if (w.local)
473
+ return;
474
+ const member = inType(ctx);
475
+ let { name, qualifier } = d;
476
+ const range = outer(node);
477
+ const end = childOfType(node, 'field_initializer_list') ?? field(node, 'body');
478
+ let sig = sigText(ctx, range, end ? end.startIndex : range.endIndex);
479
+ if (node.hasError)
480
+ sig = cutAtBrace(sig);
481
+ if (!member && !qualifier && isConstName(name)) {
482
+ const real = nameBehindMacros(name, sig);
483
+ if (real !== name) {
484
+ name = real;
485
+ }
486
+ else if (d.fn?.parent?.type === 'function_declarator') {
487
+ // `FN(jstring, nativeStart)(JNIEnv* e, ...) { ... }`: the macro call is followed by the real
488
+ // parameter list, so the macro builds the declaration head and its last argument is the name
489
+ // (the earlier ones are the return type and attributes, not a container).
490
+ const args = field(d.fn, 'parameters')?.namedChildren ?? [];
491
+ const last = args[args.length - 1];
492
+ if (last && last.namedChildCount === 1 && last.firstNamedChild?.type === 'type_identifier')
493
+ name = last.text;
494
+ }
495
+ else {
496
+ const m = macroDefinedName(d.fn);
497
+ if (m)
498
+ ({ name, qualifier } = m);
499
+ }
500
+ }
501
+ define(ctx, node, {
502
+ name,
503
+ kind: member || d.qualifier ? 'method' : 'function',
504
+ range,
505
+ sig,
506
+ qualifier,
507
+ exported: !w.anon && (member || !isStatic(node)),
508
+ });
509
+ };
510
+ /** Shared by `declaration` (file / namespace / class level) and `field_declaration`. */
511
+ function declare(node, ctx, isField) {
512
+ if (!isField)
513
+ hintDeclaration(node, ctx); // locals too
514
+ if (ctx.inCallable())
515
+ return;
516
+ const decls = node.childrenForFieldName('declarator');
517
+ if (decls.length === 0)
518
+ return;
519
+ if (node.parent?.type === 'friend_declaration')
520
+ return;
521
+ const member = inType(ctx);
522
+ if (isField && !member)
523
+ return; // members of an anonymous aggregate without a symbol
524
+ const w = where(node);
525
+ if (w.local)
526
+ return;
527
+ const range = outer(node);
528
+ const exported = !w.anon && (member || !isStatic(node));
529
+ // Macro-heavy code: a declaration with parse errors is only trusted for prototypes.
530
+ // (A lone missing ';' is harmless: 'int n_ GUARDED_BY(mu_);'.)
531
+ const broken = node.hasError && !missingSemicolon(node);
532
+ if (isField)
533
+ hintDeclaration(node, ctx);
534
+ // `int a, b, c;` members: one symbol each with signature `int a`, not the whole line.
535
+ const type = decls.length > 1 && member ? field(node, 'type') : null;
536
+ const head = type && !(CONTAINER_KIND[type.type] && field(type, 'body')) ? sigText(ctx, range, decls[0].startIndex) : '';
537
+ for (const decl of decls) {
538
+ if (!decl)
539
+ continue;
540
+ const d = declared(decl);
541
+ if (!d)
542
+ continue;
543
+ if (d.isFn) {
544
+ if (isTrailingAnnotation(node, d.name))
545
+ continue;
546
+ let sig = sigText(ctx, range, range.endIndex);
547
+ if (node.hasError)
548
+ sig = cutAtBrace(sig);
549
+ const pointer = macroFunctionPointer(d.fn);
550
+ if (pointer) {
551
+ define(ctx, node, { name: pointer, kind: member ? 'field' : 'variable', range, sig, exported, scope: false });
552
+ continue;
553
+ }
554
+ const real = isField && node.hasError && isConstName(d.name) ? annotatedField(decl) : null;
555
+ if (real) {
556
+ define(ctx, node, { name: real.text, kind: 'field', range, sig, exported, scope: false });
557
+ ctx.typeHint(real.text, typeText(field(node, 'type')));
558
+ continue;
559
+ }
560
+ define(ctx, node, {
561
+ name: d.qualifier ? d.name : nameBehindMacros(d.name, sig),
562
+ kind: member ? 'method' : 'declaration',
563
+ range,
564
+ sig,
565
+ qualifier: d.qualifier,
566
+ exported,
567
+ scope: false,
568
+ });
569
+ continue;
570
+ }
571
+ // `T (x);` is almost always a misparsed call or constructor initializer.
572
+ const inner = decl.type === 'init_declarator' ? field(decl, 'declarator') : decl;
573
+ if (broken || inner?.type === 'parenthesized_declarator')
574
+ continue;
575
+ const value = decl.type === 'init_declarator' ? field(decl, 'value') : null;
576
+ if (member) {
577
+ const sig = head && inner
578
+ ? head + ' ' + (inner.endIndex - inner.startIndex <= 80 ? inner.text.replace(/\s+/g, ' ') : d.name)
579
+ : declSig(ctx, node, range, d.name, null);
580
+ define(ctx, node, { name: d.name, kind: 'field', range, sig, exported, scope: false });
581
+ continue;
582
+ }
583
+ if (!value && isExtern(node))
584
+ continue; // forward declaration of a global defined elsewhere
585
+ if (value && value.type === 'lambda_expression' && decls.length === 1) {
586
+ const body = field(value, 'body');
587
+ define(ctx, node, {
588
+ name: d.name,
589
+ kind: 'function',
590
+ range,
591
+ sig: sigText(ctx, range, body ? body.startIndex : range.endIndex),
592
+ qualifier: d.qualifier,
593
+ exported,
594
+ });
595
+ continue;
596
+ }
597
+ define(ctx, node, {
598
+ name: d.name,
599
+ kind: isConst(node) || isConstName(d.name) ? 'constant' : 'variable',
600
+ range,
601
+ sig: declSig(ctx, node, range, d.name, value),
602
+ qualifier: d.qualifier,
603
+ exported,
604
+ scope: false,
605
+ });
606
+ }
607
+ }
608
+ /**
609
+ * `err_t (CALLCONV* find)(const char*, int)`: a function pointer whose calling convention is a macro
610
+ * parses as a function called `err_t` taking `CALLCONV* find`. The declared name is the pointer.
611
+ */
612
+ function macroFunctionPointer(fn) {
613
+ if (!fn || fn.parent?.type !== 'function_declarator')
614
+ return null;
615
+ const params = (field(fn, 'parameters')?.namedChildren ?? []).filter(c => c?.type === 'parameter_declaration');
616
+ if (params.length !== 1)
617
+ return null;
618
+ const ptr = field(params[0], 'declarator');
619
+ if (ptr?.type !== 'pointer_declarator')
620
+ return null;
621
+ const id = field(ptr, 'declarator');
622
+ return id?.type === 'identifier' ? id.text : null;
623
+ }
624
+ const declaration = (node, ctx) => declare(node, ctx, false);
625
+ const fieldDeclaration = (node, ctx) => declare(node, ctx, true);
626
+ function heritage(node, sym, ctx) {
627
+ const clause = childOfType(node, 'base_class_clause');
628
+ if (!clause)
629
+ return;
630
+ for (const b of clause.namedChildren) {
631
+ if (!b || b.type === 'access_specifier')
632
+ continue;
633
+ const name = typeName(b);
634
+ if (name)
635
+ ctx.inherit(sym, name, 'extends');
636
+ }
637
+ }
638
+ const container = (node, ctx) => {
639
+ const body = field(node, 'body');
640
+ const nameNode = field(node, 'name');
641
+ // No body: a type reference. No name: anonymous, or named by an enclosing typedef.
642
+ if (!body || !nameNode || ctx.inCallable())
643
+ return;
644
+ const named = leafName(nameNode, null); // `struct Outer::Inner { }` keeps its qualifier
645
+ if (!named)
646
+ return;
647
+ const w = where(node);
648
+ if (w.local)
649
+ return;
650
+ const p = node.parent;
651
+ const wrapped = p && (p.type === 'type_definition' || p.type === 'declaration' || p.type === 'field_declaration');
652
+ const range = outer(wrapped ? p : node);
653
+ if (named.qualifier)
654
+ for (const q of named.qualifier.split('::'))
655
+ ctx.scope(q);
656
+ const sym = define(ctx, node, {
657
+ name: named.name,
658
+ kind: CONTAINER_KIND[node.type] ?? 'struct',
659
+ range,
660
+ sig: sigText(ctx, range, body.startIndex),
661
+ exported: !w.anon,
662
+ });
663
+ heritage(node, sym, ctx);
664
+ };
665
+ const typeDefinition = (node, ctx) => {
666
+ if (ctx.inCallable())
667
+ return;
668
+ const w = where(node);
669
+ if (w.local)
670
+ return;
671
+ const type = field(node, 'type');
672
+ const kind = type ? CONTAINER_KIND[type.type] : undefined;
673
+ const body = kind ? field(type, 'body') : null;
674
+ const tagName = body ? field(type, 'name')?.text : undefined;
675
+ const head = body ? sigText(ctx, node, body.startIndex) + ' { ... } ' : '';
676
+ // `typedef struct { ... } Foo;` - the first plain declarator names the aggregate.
677
+ let adopt = null;
678
+ for (const decl of node.childrenForFieldName('declarator')) {
679
+ if (!decl)
680
+ continue;
681
+ const d = declared(decl);
682
+ if (!d)
683
+ continue;
684
+ if (body && !tagName && !adopt && decl.type === 'type_identifier') {
685
+ adopt = { name: d.name };
686
+ continue;
687
+ }
688
+ if (d.name === tagName && decl.type === 'type_identifier')
689
+ continue; // typedef struct foo {...} foo;
690
+ define(ctx, node, {
691
+ name: d.name,
692
+ kind: 'type',
693
+ sig: body ? head + (decl.endIndex - decl.startIndex < 80 ? decl.text : d.name) : sigText(ctx, node, node.endIndex),
694
+ exported: !w.anon,
695
+ scope: false,
696
+ });
697
+ }
698
+ // Opened last so the aliases above are not nested under it; members are.
699
+ if (adopt)
700
+ define(ctx, node, { name: adopt.name, kind: kind, sig: head + adopt.name, exported: !w.anon });
701
+ };
702
+ const aliasDeclaration = (node, ctx) => {
703
+ const name = field(node, 'name');
704
+ if (!name || ctx.inCallable())
705
+ return;
706
+ const w = where(node);
707
+ if (w.local)
708
+ return;
709
+ const range = outer(node);
710
+ define(ctx, node, { name: name.text, kind: 'type', range, sig: sigText(ctx, range, range.endIndex), exported: !w.anon, scope: false });
711
+ };
712
+ const enumerator = (node, ctx) => {
713
+ const name = field(node, 'name');
714
+ if (!name || ctx.inCallable())
715
+ return;
716
+ // Enumerators of an anonymous enum land in the enclosing scope.
717
+ if (ctx.parentKind() !== 'enum' && where(node).local)
718
+ return;
719
+ define(ctx, node, { name: name.text, kind: 'constant', sig: sigText(ctx, node, node.endIndex), exported: true, scope: false });
720
+ };
721
+ const namespaceDefinition = (node, ctx) => {
722
+ const nameNode = field(node, 'name');
723
+ if (!nameNode)
724
+ return; // anonymous namespace: no symbol, no scope
725
+ const parts = nameNode.text.split('::').map(s => s.trim()).filter(Boolean);
726
+ const name = parts.pop();
727
+ if (!name)
728
+ return;
729
+ for (const p of parts)
730
+ ctx.scope(p); // namespace a::b { }
731
+ define(ctx, node, { name, kind: 'namespace', sig: sigText(ctx, node, nameNode.endIndex), exported: !where(node).anon });
732
+ };
733
+ const macro = (node, ctx) => {
734
+ const nameNode = field(node, 'name');
735
+ if (!nameNode || ctx.inCallable())
736
+ return;
737
+ const name = nameNode.text;
738
+ const params = field(node, 'parameters');
739
+ let sig = '#define ' + name;
740
+ if (params) {
741
+ sig += params.text;
742
+ }
743
+ else {
744
+ const value = field(node, 'value');
745
+ if (!value) {
746
+ if (/_H(PP|XX)?_*$/i.test(name))
747
+ return; // include guard
748
+ }
749
+ else if (value.endIndex - value.startIndex <= 80) {
750
+ const v = value.text.trim();
751
+ if (!v.includes('\n'))
752
+ sig += ' ' + v;
753
+ }
754
+ }
755
+ // A function-like macro that wraps calls (`#define serverAssert(e) (... _serverAssert(#e, __FILE__, __LINE__))`):
756
+ // the calls in its body are calls made by the macro, so the wrapped function does not look unused.
757
+ const body = params ? field(node, 'value') : null;
758
+ const text = body && body.endIndex - body.startIndex <= 2000 ? body.text : '';
759
+ const called = new Set();
760
+ if (text) {
761
+ const own = new Set(params.text.replace(/[()\s]/g, '').split(','));
762
+ const re = /(^|[^\w.>])([A-Za-z_]\w*)\s*\(/g;
763
+ for (let m = re.exec(text); m && called.size < 12; m = re.exec(text)) {
764
+ const callee = m[2];
765
+ if (callee !== name && !own.has(callee) && !MACRO_BODY_SKIP.has(callee))
766
+ called.add(callee);
767
+ }
768
+ }
769
+ define(ctx, node, { name, kind: 'macro', sig, exported: true, scope: called.size > 0 });
770
+ for (const callee of called)
771
+ ctx.call(callee, node);
772
+ };
773
+ const MACRO_BODY_SKIP = new Set([
774
+ 'if', 'while', 'for', 'switch', 'return', 'sizeof', 'do', 'else', 'defined', 'typeof', '__typeof__', 'alignof',
775
+ '_Alignof', 'offsetof', '__attribute__', '__builtin_expect', 'static_assert', '_Static_assert', 'decltype',
776
+ ]);
777
+ const include = (node, ctx) => {
778
+ const path = field(node, 'path');
779
+ if (!path || (path.type !== 'string_literal' && path.type !== 'system_lib_string'))
780
+ return;
781
+ // System includes keep their brackets (`<math.h>`) so they are never matched to project files.
782
+ ctx.imp(path.type === 'system_lib_string' ? path.text : stripQuotes(path.text), node);
783
+ };
784
+ const callExpression = (node, ctx) => {
785
+ const fn = field(node, 'function');
786
+ if (!fn)
787
+ return;
788
+ switch (fn.type) {
789
+ case 'identifier':
790
+ ctx.call(fn.text, node);
791
+ break;
792
+ case 'field_expression':
793
+ ctx.call(typeName(field(fn, 'field')), node, simpleReceiver(field(fn, 'argument')));
794
+ break;
795
+ case 'qualified_identifier': {
796
+ const scopes = [];
797
+ let cur = fn;
798
+ for (let i = 0; cur && cur.type === 'qualified_identifier' && i < 12; i++) {
799
+ const s = typeName(field(cur, 'scope'));
800
+ if (s)
801
+ scopes.push(s);
802
+ cur = field(cur, 'name');
803
+ }
804
+ if (!cur)
805
+ break;
806
+ const name = cur.type === 'destructor_name' ? cur.text : typeName(cur);
807
+ ctx.call(name, node, scopes.length ? scopes.join('::') : null);
808
+ break;
809
+ }
810
+ case 'template_function':
811
+ ctx.call(typeName(fn), node);
812
+ break;
813
+ }
814
+ };
815
+ const newExpression = (node, ctx) => {
816
+ ctx.call(typeName(field(node, 'type')), node);
817
+ };
818
+ // ---------------------------------------------------------------------------
819
+ // Specs
820
+ // ---------------------------------------------------------------------------
821
+ const cVisitors = {
822
+ function_definition: functionDefinition,
823
+ declaration,
824
+ field_declaration: fieldDeclaration,
825
+ struct_specifier: container,
826
+ union_specifier: container,
827
+ enum_specifier: container,
828
+ enumerator,
829
+ type_definition: typeDefinition,
830
+ preproc_def: macro,
831
+ preproc_function_def: macro,
832
+ preproc_include: include,
833
+ call_expression: callExpression,
834
+ parameter_declaration: parameter,
835
+ };
836
+ const cppVisitors = {
837
+ ...cVisitors,
838
+ class_specifier: container,
839
+ alias_declaration: aliasDeclaration,
840
+ namespace_definition: namespaceDefinition,
841
+ new_expression: newExpression,
842
+ optional_parameter_declaration: parameter,
843
+ for_range_loop: forRange,
844
+ };
845
+ const C_IDENTIFIERS = ['identifier', 'type_identifier', 'field_identifier'];
846
+ const CPP_HINT = new RegExp([
847
+ String.raw `^[ \t]*(?:template\s*<[^;{]*>\s*)?class\s+[A-Za-z_]\w*\s*(?:final\s*)?(?:[:{;]|$)`,
848
+ String.raw `^[ \t]*(?:inline\s+)?namespace(?:\s+[\w:]+)?\s*\{`,
849
+ String.raw `^[ \t]*using\s+(?:namespace\s+)?[\w:]+\s*[;=]`,
850
+ String.raw `^[ \t]*template\s*<`,
851
+ String.raw `^[ \t]*(?:public|private|protected)\s*:`,
852
+ String.raw `[A-Za-z_]\w*::[A-Za-z_~]`,
853
+ String.raw `^[ \t]*#[ \t]*include[ \t]*<(?:string|vector|memory|map|set|iostream|algorithm|functional|utility|cstdint|cstddef|cstdio|cstdlib|cstring|atomic|mutex|array|unordered_map|type_traits)>`,
854
+ ].join('|'), 'm');
855
+ export const c = {
856
+ id: 'c',
857
+ lowercaseTypes: true,
858
+ grammar: 'c',
859
+ // .def: X-macro tables included by C sources (command tables, opcode lists).
860
+ extensions: ['.c', '.h', '.def'],
861
+ identifiers: C_IDENTIFIERS,
862
+ visit: cVisitors,
863
+ /** `.h` files are shared with C++: parse them as C++ when they look like it. */
864
+ sniff(source, path) {
865
+ if (!/\.h$/i.test(path))
866
+ return undefined;
867
+ const head = source.length > 20000 ? source.slice(0, 20000) : source;
868
+ return CPP_HINT.test(head) ? 'cpp' : undefined;
869
+ },
870
+ };
871
+ export const cpp = {
872
+ id: 'cpp',
873
+ lowercaseTypes: true,
874
+ grammar: 'cpp',
875
+ extensions: ['.cpp', '.hpp', '.cc', '.hh', '.cxx', '.hxx', '.inl', '.ipp'],
876
+ identifiers: [...C_IDENTIFIERS, 'namespace_identifier'],
877
+ visit: cppVisitors,
878
+ };
879
+ //# sourceMappingURL=c.js.map