code-auditor-mcp 3.4.11 → 3.4.13

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 (254) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +119 -1
  3. package/dist/analyzers/__verify_self_probe.d.ts +2 -0
  4. package/dist/analyzers/__verify_self_probe.d.ts.map +1 -0
  5. package/dist/analyzers/__verify_self_probe.js +5 -0
  6. package/dist/analyzers/__verify_self_probe.js.map +1 -0
  7. package/dist/analyzers/analyzerUtils.d.ts +34 -1
  8. package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
  9. package/dist/analyzers/analyzerUtils.js +61 -22
  10. package/dist/analyzers/analyzerUtils.js.map +1 -1
  11. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -23
  12. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  13. package/dist/analyzers/cross-language/APIContractAnalyzer.js +198 -186
  14. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  15. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +78 -55
  16. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  17. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +244 -243
  18. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  19. package/dist/analyzers/cross-language/SchemaValidator.d.ts +27 -37
  20. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  21. package/dist/analyzers/cross-language/SchemaValidator.js +314 -275
  22. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  23. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +17 -46
  24. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
  25. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +620 -523
  26. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
  27. package/dist/analyzers/documentationAnalyzer.d.ts +7 -2
  28. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  29. package/dist/analyzers/documentationAnalyzer.js +259 -198
  30. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  31. package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
  32. package/dist/analyzers/invariantsAnalyzer.js +59 -46
  33. package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
  34. package/dist/analyzers/orm/adapterRegistry.d.ts +10 -0
  35. package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -1
  36. package/dist/analyzers/orm/adapterRegistry.js +10 -0
  37. package/dist/analyzers/orm/adapterRegistry.js.map +1 -1
  38. package/dist/analyzers/orm/drizzleAdapter.d.ts +22 -0
  39. package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
  40. package/dist/analyzers/orm/drizzleAdapter.js +42 -44
  41. package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
  42. package/dist/analyzers/orm/prismaAdapter.d.ts +30 -0
  43. package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -1
  44. package/dist/analyzers/orm/prismaAdapter.js +30 -0
  45. package/dist/analyzers/orm/prismaAdapter.js.map +1 -1
  46. package/dist/analyzers/provenance.d.ts +72 -2
  47. package/dist/analyzers/provenance.d.ts.map +1 -1
  48. package/dist/analyzers/provenance.js +462 -338
  49. package/dist/analyzers/provenance.js.map +1 -1
  50. package/dist/analyzers/reactAnalyzer.d.ts +11 -0
  51. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  52. package/dist/analyzers/reactAnalyzer.js +188 -125
  53. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  54. package/dist/analyzers/ruleRegistry.d.ts +118 -1
  55. package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
  56. package/dist/analyzers/ruleRegistry.js +1717 -104
  57. package/dist/analyzers/ruleRegistry.js.map +1 -1
  58. package/dist/analyzers/ruleTiming.d.ts +54 -0
  59. package/dist/analyzers/ruleTiming.d.ts.map +1 -0
  60. package/dist/analyzers/ruleTiming.js +71 -0
  61. package/dist/analyzers/ruleTiming.js.map +1 -0
  62. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +13 -0
  63. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
  64. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +362 -283
  65. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
  66. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +26 -68
  67. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  68. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +430 -391
  69. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  70. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +24 -186
  71. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  72. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +1081 -1061
  73. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  74. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +3 -49
  75. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  76. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +414 -340
  77. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  78. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +48 -5
  79. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  80. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +262 -134
  81. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  82. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +23 -212
  83. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  84. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +139 -1518
  85. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  86. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +78 -79
  87. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
  88. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +613 -472
  89. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
  90. package/dist/analyzers/universal/schema/codeAnalysis.d.ts +307 -0
  91. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -0
  92. package/dist/analyzers/universal/schema/codeAnalysis.js +965 -0
  93. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -0
  94. package/dist/analyzers/universal/schema/config.d.ts +31 -0
  95. package/dist/analyzers/universal/schema/config.d.ts.map +1 -0
  96. package/dist/analyzers/universal/schema/config.js +57 -0
  97. package/dist/analyzers/universal/schema/config.js.map +1 -0
  98. package/dist/analyzers/universal/schema/discovery.d.ts +167 -0
  99. package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -0
  100. package/dist/analyzers/universal/schema/discovery.js +616 -0
  101. package/dist/analyzers/universal/schema/discovery.js.map +1 -0
  102. package/dist/analyzers/universal/schema/jsonSchema.d.ts +27 -0
  103. package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -0
  104. package/dist/analyzers/universal/schema/jsonSchema.js +368 -0
  105. package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -0
  106. package/dist/analyzers/universal/schema/migrations.d.ts +90 -0
  107. package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -0
  108. package/dist/analyzers/universal/schema/migrations.js +238 -0
  109. package/dist/analyzers/universal/schema/migrations.js.map +1 -0
  110. package/dist/analyzers/universal/schema/types.d.ts +131 -0
  111. package/dist/analyzers/universal/schema/types.d.ts.map +1 -0
  112. package/dist/analyzers/universal/schema/types.js +10 -0
  113. package/dist/analyzers/universal/schema/types.js.map +1 -0
  114. package/dist/analyzers/universal/schema/violations.d.ts +44 -0
  115. package/dist/analyzers/universal/schema/violations.d.ts.map +1 -0
  116. package/dist/analyzers/universal/schema/violations.js +54 -0
  117. package/dist/analyzers/universal/schema/violations.js.map +1 -0
  118. package/dist/auditRunner.d.ts.map +1 -1
  119. package/dist/auditRunner.js +101 -39
  120. package/dist/auditRunner.js.map +1 -1
  121. package/dist/cli.js +348 -40
  122. package/dist/cli.js.map +1 -1
  123. package/dist/config/configLoader.d.ts.map +1 -1
  124. package/dist/config/configLoader.js +9 -7
  125. package/dist/config/configLoader.js.map +1 -1
  126. package/dist/config/defaults.d.ts +12 -3
  127. package/dist/config/defaults.d.ts.map +1 -1
  128. package/dist/config/defaults.js +15 -5
  129. package/dist/config/defaults.js.map +1 -1
  130. package/dist/config/effectiveConfig.d.ts +75 -0
  131. package/dist/config/effectiveConfig.d.ts.map +1 -0
  132. package/dist/config/effectiveConfig.js +213 -0
  133. package/dist/config/effectiveConfig.js.map +1 -0
  134. package/dist/config/pathProfiles.d.ts +7 -4
  135. package/dist/config/pathProfiles.d.ts.map +1 -1
  136. package/dist/config/pathProfiles.js +8 -10
  137. package/dist/config/pathProfiles.js.map +1 -1
  138. package/dist/config/thresholdRationales.d.ts +45 -0
  139. package/dist/config/thresholdRationales.d.ts.map +1 -0
  140. package/dist/config/thresholdRationales.js +67 -0
  141. package/dist/config/thresholdRationales.js.map +1 -0
  142. package/dist/conventions/conventionMiner.d.ts.map +1 -1
  143. package/dist/conventions/conventionMiner.js +6 -2
  144. package/dist/conventions/conventionMiner.js.map +1 -1
  145. package/dist/enforcement/diffGate.d.ts +55 -0
  146. package/dist/enforcement/diffGate.d.ts.map +1 -0
  147. package/dist/enforcement/diffGate.js +136 -0
  148. package/dist/enforcement/diffGate.js.map +1 -0
  149. package/dist/enforcement/gate.d.ts +36 -0
  150. package/dist/enforcement/gate.d.ts.map +1 -0
  151. package/dist/enforcement/gate.js +72 -0
  152. package/dist/enforcement/gate.js.map +1 -0
  153. package/dist/enforcement/suppressions.d.ts +41 -0
  154. package/dist/enforcement/suppressions.d.ts.map +1 -0
  155. package/dist/enforcement/suppressions.js +170 -0
  156. package/dist/enforcement/suppressions.js.map +1 -0
  157. package/dist/fingerprint.d.ts.map +1 -1
  158. package/dist/fingerprint.js +4 -1
  159. package/dist/fingerprint.js.map +1 -1
  160. package/dist/grammars/tree-sitter-scss.wasm +0 -0
  161. package/dist/invariants/ruleEngine.d.ts +3 -4
  162. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  163. package/dist/invariants/ruleEngine.js +2 -2
  164. package/dist/invariants/ruleEngine.js.map +1 -1
  165. package/dist/languages/LanguageOrchestrator.d.ts +14 -0
  166. package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
  167. package/dist/languages/LanguageOrchestrator.js +93 -55
  168. package/dist/languages/LanguageOrchestrator.js.map +1 -1
  169. package/dist/languages/LanguageRegistry.d.ts +8 -0
  170. package/dist/languages/LanguageRegistry.d.ts.map +1 -1
  171. package/dist/languages/LanguageRegistry.js +8 -0
  172. package/dist/languages/LanguageRegistry.js.map +1 -1
  173. package/dist/languages/RuntimeManager.d.ts +82 -60
  174. package/dist/languages/RuntimeManager.d.ts.map +1 -1
  175. package/dist/languages/RuntimeManager.js +404 -349
  176. package/dist/languages/RuntimeManager.js.map +1 -1
  177. package/dist/languages/UniversalAnalyzer.d.ts +51 -6
  178. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  179. package/dist/languages/UniversalAnalyzer.js +127 -88
  180. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  181. package/dist/languages/adapterBridge.d.ts +36 -0
  182. package/dist/languages/adapterBridge.d.ts.map +1 -1
  183. package/dist/languages/adapterBridge.js +89 -35
  184. package/dist/languages/adapterBridge.js.map +1 -1
  185. package/dist/languages/go/GoAdapter.d.ts +66 -23
  186. package/dist/languages/go/GoAdapter.d.ts.map +1 -1
  187. package/dist/languages/go/GoAdapter.js +553 -474
  188. package/dist/languages/go/GoAdapter.js.map +1 -1
  189. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +38 -9
  190. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -1
  191. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +111 -74
  192. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
  193. package/dist/languages/tree-sitter/converter.d.ts +18 -0
  194. package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
  195. package/dist/languages/tree-sitter/converter.js +18 -0
  196. package/dist/languages/tree-sitter/converter.js.map +1 -1
  197. package/dist/languages/tree-sitter/parser.d.ts +39 -2
  198. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  199. package/dist/languages/tree-sitter/parser.js +95 -12
  200. package/dist/languages/tree-sitter/parser.js.map +1 -1
  201. package/dist/languages/types.d.ts +67 -2
  202. package/dist/languages/types.d.ts.map +1 -1
  203. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +253 -62
  204. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  205. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +1461 -791
  206. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  207. package/dist/mcp-tools/whitelistTools.d.ts +1 -1
  208. package/dist/mcp-tools-shared.d.ts.map +1 -1
  209. package/dist/mcp-tools-shared.js +1 -0
  210. package/dist/mcp-tools-shared.js.map +1 -1
  211. package/dist/mcpAuditJobs.d.ts.map +1 -1
  212. package/dist/mcpAuditJobs.js +1 -0
  213. package/dist/mcpAuditJobs.js.map +1 -1
  214. package/dist/pipeline.d.ts +30 -1
  215. package/dist/pipeline.d.ts.map +1 -1
  216. package/dist/pipeline.js +527 -203
  217. package/dist/pipeline.js.map +1 -1
  218. package/dist/pipelineAdapters.d.ts +5 -12
  219. package/dist/pipelineAdapters.d.ts.map +1 -1
  220. package/dist/pipelineAdapters.js +175 -100
  221. package/dist/pipelineAdapters.js.map +1 -1
  222. package/dist/presets/presets.d.ts +54 -0
  223. package/dist/presets/presets.d.ts.map +1 -0
  224. package/dist/presets/presets.js +181 -0
  225. package/dist/presets/presets.js.map +1 -0
  226. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  227. package/dist/reporting/sarifReportGenerator.js +0 -2
  228. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  229. package/dist/ruleAliases.d.ts +67 -0
  230. package/dist/ruleAliases.d.ts.map +1 -0
  231. package/dist/ruleAliases.js +79 -0
  232. package/dist/ruleAliases.js.map +1 -0
  233. package/dist/styles/cssAstExtractor.d.ts +20 -5
  234. package/dist/styles/cssAstExtractor.d.ts.map +1 -1
  235. package/dist/styles/cssAstExtractor.js +247 -9
  236. package/dist/styles/cssAstExtractor.js.map +1 -1
  237. package/dist/styles/styleIndexer.js +13 -4
  238. package/dist/styles/styleIndexer.js.map +1 -1
  239. package/dist/styles/tailwindConfigLoader.d.ts +8 -6
  240. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
  241. package/dist/styles/tailwindConfigLoader.js +61 -15
  242. package/dist/styles/tailwindConfigLoader.js.map +1 -1
  243. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  244. package/dist/styles/tailwindUtilityExpander.js +13 -1
  245. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  246. package/dist/types.d.ts +201 -1
  247. package/dist/types.d.ts.map +1 -1
  248. package/dist/types.js +8 -0
  249. package/dist/types.js.map +1 -1
  250. package/package.json +7 -4
  251. package/plugin/.claude-plugin/plugin.json +1 -1
  252. package/plugin/hooks/hooks.json +1 -1
  253. package/plugin/scripts/hook-audit.sh +25 -7
  254. package/plugin/skills/code-auditor/SKILL.md +5 -5
@@ -3,330 +3,156 @@
3
3
  *
4
4
  * Provides real AST-based analysis for Go source files. Replaces the previous
5
5
  * regex-based stub that returned empty arrays for most queries.
6
+ *
7
+ * The class is split across a small inheritance chain (Spec-33
8
+ * interface-segregation): each link contributes a cohesive slice of the
9
+ * adapter so no single class exceeds the SOLID class-size threshold. The
10
+ * chain is ordered leaf-first (traversal/name/doc/build helpers) so every
11
+ * method's callee lives in an ancestor class.
6
12
  */
7
- import { getParser } from '../tree-sitter/parser.js';
13
+ import { getParser, parseWithRecovery } from '../tree-sitter/parser.js';
8
14
  import { toASTNode, toSourceLocation } from '../tree-sitter/converter.js';
9
15
  // ---------------------------------------------------------------------------
10
16
  // Source code storage
11
17
  // ---------------------------------------------------------------------------
12
18
  const sourceCodeMap = new WeakMap();
13
19
  // ---------------------------------------------------------------------------
14
- // Adapter
20
+ // Traversal + name/documentation + Go-specific helpers slice (leaf helpers,
21
+ // called across later chunks, so they form the base of the chain)
15
22
  // ---------------------------------------------------------------------------
16
- export class TreeSitterGoAdapter {
17
- name = 'go';
18
- fileExtensions = ['.go'];
19
- // -- File detection -------------------------------------------------------
20
- supportsFile(filePath) {
21
- return filePath.endsWith('.go');
22
- }
23
- // -- Parsing --------------------------------------------------------------
24
- async parse(filePath, content) {
25
- const parser = getParser('go');
26
- const tree = parser.parse(content);
27
- if (!tree)
28
- throw new Error(`Failed to parse Go file: ${filePath}`);
29
- const errors = [];
30
- this.collectErrors(tree.rootNode, errors);
31
- const root = toASTNode(tree.rootNode, undefined, 'go');
32
- const ast = {
33
- root,
34
- language: 'go',
35
- filePath,
36
- errors,
37
- };
38
- sourceCodeMap.set(ast, content);
39
- return ast;
40
- }
41
- // -- AST Navigation -------------------------------------------------------
42
- findNodes(ast, pattern) {
43
- const results = [];
44
- this.walk(ast.root, (node) => {
45
- if (this.matchesPattern(node, pattern)) {
46
- results.push(node);
47
- }
48
- });
49
- return results;
50
- }
51
- getParent(node) {
52
- return node.parent ?? null;
53
- }
54
- getChildren(node) {
55
- return node.children ?? [];
56
- }
57
- getSiblings(node) {
58
- if (!node.parent?.children)
59
- return [];
60
- return node.parent.children.filter((c) => c !== node);
61
- }
62
- // -- Node Information -----------------------------------------------------
63
- getNodeType(node) {
64
- return node.type;
65
- }
66
- getNodeText(node, sourceCode) {
67
- return sourceCode.slice(node.range[0], node.range[1]);
68
- }
69
- getNodeName(node) {
70
- return this.extractName(node.raw);
71
- }
72
- getNodeLocation(node) {
73
- return node.location;
74
- }
75
- // -- Language-Specific Extraction -----------------------------------------
76
- extractFunctions(ast) {
77
- const sourceCode = sourceCodeMap.get(ast) ?? '';
78
- const functions = [];
79
- this.walk(ast.root, (astNode) => {
80
- const node = astNode.raw;
81
- if (node.type === 'function_declaration') {
82
- const fn = this.buildFunctionInfo(node, sourceCode);
83
- if (fn)
84
- functions.push(fn);
85
- }
86
- });
87
- return functions;
88
- }
89
- extractClasses(ast) {
90
- const sourceCode = sourceCodeMap.get(ast) ?? '';
91
- const classes = [];
92
- // Collect all function_declarations so we can match methods to structs
93
- const allFunctions = this.extractFunctions(ast);
94
- this.walk(ast.root, (astNode) => {
95
- const node = astNode.raw;
96
- if (node.type === 'type_declaration') {
97
- const specs = this.findNamedChildren(node, 'type_spec');
98
- for (const spec of specs) {
99
- const nameNode = spec.childForFieldName?.('name');
100
- if (!nameNode)
101
- continue;
102
- const typeNode = spec.childForFieldName?.('type');
103
- if (!typeNode || typeNode.type !== 'struct_type')
104
- continue;
105
- const structInfo = this.buildStructAsClass(spec, nameNode, typeNode, allFunctions, sourceCode);
106
- if (structInfo)
107
- classes.push(structInfo);
108
- }
109
- }
110
- });
111
- return classes;
112
- }
113
- extractImports(ast) {
114
- const imports = [];
115
- this.walk(ast.root, (astNode) => {
116
- const node = astNode.raw;
117
- if (node.type === 'import_declaration') {
118
- for (const spec of node.namedChildren) {
119
- if (spec.type === 'import_spec') {
120
- const imp = this.buildImportInfo(spec);
121
- if (imp)
122
- imports.push(imp);
123
- }
124
- }
125
- }
126
- });
127
- return imports;
128
- }
129
- extractExports(ast) {
130
- const exports = [];
131
- this.walk(ast.root, (astNode) => {
132
- const node = astNode.raw;
133
- if (node.type === 'function_declaration') {
134
- const name = this.extractName(node);
135
- if (name && this.isExportedGo(name)) {
136
- exports.push({
137
- name,
138
- location: toSourceLocation(node),
139
- isDefault: false,
140
- });
141
- }
142
- }
143
- else if (node.type === 'type_declaration') {
144
- for (const spec of this.findNamedChildren(node, 'type_spec')) {
145
- const nameNode = spec.childForFieldName?.('name');
146
- if (nameNode && this.isExportedGo(nameNode.text)) {
147
- exports.push({
148
- name: nameNode.text,
149
- location: toSourceLocation(nameNode),
150
- isDefault: false,
151
- });
152
- }
153
- }
154
- }
155
- else if (node.type === 'var_declaration') {
156
- for (const spec of this.findNamedChildren(node, 'var_spec')) {
157
- const nameNode = spec.childForFieldName?.('name');
158
- if (nameNode && this.isExportedGo(nameNode.text)) {
159
- exports.push({
160
- name: nameNode.text,
161
- location: toSourceLocation(nameNode),
162
- isDefault: false,
163
- });
164
- }
165
- }
23
+ class GoTraversalHelpers {
24
+ // -- Tree traversal helpers ------------------------------------------------
25
+ walk(node, visitor) {
26
+ visitor(node);
27
+ if (node.children) {
28
+ for (const child of node.children) {
29
+ this.walk(child, visitor);
166
30
  }
167
- });
168
- return exports;
169
- }
170
- // -- Pattern Matching Helpers ---------------------------------------------
171
- isClass(node) {
172
- const n = node.raw;
173
- if (n.type === 'type_spec') {
174
- const typeNode = n.childForFieldName?.('type');
175
- return typeNode?.type === 'struct_type';
176
31
  }
177
- return n.type === 'struct_type';
178
32
  }
179
- isFunction(node) {
180
- return node.raw.type === 'function_declaration';
181
- }
182
- isMethod(node) {
183
- const n = node.raw;
184
- if (n.type !== 'function_declaration')
185
- return false;
186
- return n.childForFieldName?.('receiver') != null;
187
- }
188
- isInterface(node) {
189
- const n = node.raw;
190
- if (n.type === 'type_spec') {
191
- const typeNode = n.childForFieldName?.('type');
192
- return typeNode?.type === 'interface_type';
33
+ walkRaw(node, visitor) {
34
+ visitor(node);
35
+ for (const child of node.children) {
36
+ this.walkRaw(child, visitor);
193
37
  }
194
- return n.type === 'interface_type';
195
- }
196
- isImport(node) {
197
- return node.raw.type === 'import_declaration';
198
- }
199
- isExport(node) {
200
- return false; // Go has no export keyword
201
- }
202
- isLoop(node) {
203
- return node.raw.type === 'for_statement';
204
- }
205
- isConditional(node) {
206
- const t = node.raw.type;
207
- return (t === 'if_statement' ||
208
- t === 'switch_statement' ||
209
- t === 'expression_switch_statement' ||
210
- t === 'type_switch_statement');
211
- }
212
- isVariableDeclaration(node) {
213
- const t = node.raw.type;
214
- return (t === 'var_declaration' ||
215
- t === 'short_var_declaration' ||
216
- t === 'var_spec' ||
217
- t === 'const_declaration');
218
38
  }
219
- // -- Advanced Features ----------------------------------------------------
220
- getTypeInfo(node) {
221
- const n = node.raw;
222
- if (n.type === 'function_declaration') {
223
- const result = n.childForFieldName?.('result');
224
- if (result)
225
- return result.text.trim();
226
- }
227
- if (n.type === 'var_spec' || n.type === 'const_spec') {
228
- const typeNode = n.childForFieldName?.('type');
229
- if (typeNode)
230
- return typeNode.text.trim();
39
+ collectErrors(node, errors) {
40
+ if (node.type === 'ERROR' || node.isError) {
41
+ errors.push({
42
+ message: `Parse error near "${node.text.slice(0, 50)}"`,
43
+ location: toSourceLocation(node),
44
+ severity: 'error',
45
+ });
231
46
  }
232
- if (n.type === 'field_declaration') {
233
- const typeNode = n.childForFieldName?.('type');
234
- if (typeNode)
235
- return typeNode.text.trim();
47
+ for (const child of node.children) {
48
+ this.collectErrors(child, errors);
236
49
  }
237
- return null;
238
- }
239
- getDocumentation(node) {
240
- return this.extractDocumentation(node.raw);
241
- }
242
- getComplexity(node) {
243
- return this.calculateComplexity(node.raw);
244
50
  }
245
- // -- Optional: Interfaces -------------------------------------------------
246
- extractInterfaces(ast) {
247
- const interfaces = [];
248
- this.walk(ast.root, (astNode) => {
249
- const node = astNode.raw;
250
- if (node.type === 'type_declaration') {
251
- const specs = this.findNamedChildren(node, 'type_spec');
252
- for (const spec of specs) {
253
- const nameNode = spec.childForFieldName?.('name');
254
- const typeNode = spec.childForFieldName?.('type');
255
- if (nameNode && typeNode?.type === 'interface_type') {
256
- const ifaceInfo = this.buildInterfaceInfo(nameNode, typeNode);
257
- if (ifaceInfo)
258
- interfaces.push(ifaceInfo);
259
- }
260
- }
51
+ extractName(node) {
52
+ switch (node.type) {
53
+ case 'function_declaration':
54
+ case 'type_spec':
55
+ case 'field_declaration':
56
+ case 'method_spec': {
57
+ const nameNode = node.childForFieldName?.('name');
58
+ if (nameNode)
59
+ return nameNode.text;
60
+ return null;
261
61
  }
262
- });
263
- return interfaces;
264
- }
265
- // -- Optional: Raw imports ------------------------------------------------
266
- extractRawImports(_filePath, content) {
267
- const results = [];
268
- const parser = getParser('go');
269
- const tree = parser.parse(content);
270
- if (!tree)
271
- return results;
272
- this.walkRaw(tree.rootNode, (node) => {
273
- if (node.type === 'import_declaration') {
274
- for (const spec of node.namedChildren) {
275
- if (spec.type === 'import_spec') {
276
- for (const child of spec.namedChildren) {
277
- if (child.type === 'interpreted_string_literal' ||
278
- child.type === 'raw_string_literal') {
279
- results.push({
280
- moduleSpecifier: child.text.slice(1, -1),
281
- isStatic: true,
282
- isDynamic: false,
283
- isRequire: false,
284
- line: child.startPosition.row,
285
- });
286
- }
287
- }
62
+ case 'import_spec': {
63
+ const nameNode = node.childForFieldName?.('name');
64
+ if (nameNode)
65
+ return nameNode.text;
66
+ for (const child of node.namedChildren) {
67
+ if (child.type === 'interpreted_string_literal' ||
68
+ child.type === 'raw_string_literal') {
69
+ return child.text.slice(1, -1);
288
70
  }
289
71
  }
72
+ return null;
290
73
  }
291
- });
292
- return results;
293
- }
294
- // -- Optional: Exported symbols -------------------------------------------
295
- extractExportedSymbols(ast) {
296
- return this.extractExports(ast).map((e) => ({
297
- name: e.name,
298
- line: e.location.start.line,
299
- }));
74
+ case 'package_identifier':
75
+ case 'identifier':
76
+ return node.text;
77
+ default:
78
+ return null;
79
+ }
300
80
  }
301
- // ---------------------------------------------------------------------------
302
- // Internal helpers
303
- // ---------------------------------------------------------------------------
304
- walk(node, visitor) {
305
- visitor(node);
306
- if (node.children) {
307
- for (const child of node.children) {
308
- this.walk(child, visitor);
309
- }
81
+ cleanCommentText(comment) {
82
+ if (comment.startsWith('/*')) {
83
+ let inner = comment.slice(2, -2);
84
+ inner = inner
85
+ .split('\n')
86
+ .map((line) => line.replace(/^\s*\*\s?/, ''))
87
+ .join('\n');
88
+ return inner.trim();
310
89
  }
90
+ if (comment.startsWith('//')) {
91
+ return comment.replace(/^\/\/\s*/, '').trim();
92
+ }
93
+ return comment.trim();
311
94
  }
312
- walkRaw(node, visitor) {
313
- visitor(node);
95
+ getOperator(node) {
314
96
  for (const child of node.children) {
315
- this.walkRaw(child, visitor);
97
+ if (!child.isNamed &&
98
+ ['&&', '||', '+', '-', '*', '/', '%', '==', '!='].includes(child.type)) {
99
+ return child.type;
100
+ }
316
101
  }
102
+ return null;
317
103
  }
318
- collectErrors(node, errors) {
319
- if (node.type === 'ERROR' || node.isError) {
320
- errors.push({
321
- message: `Parse error near "${node.text.slice(0, 50)}"`,
322
- location: toSourceLocation(node),
323
- severity: 'error',
324
- });
104
+ // -- Go-specific helpers ---------------------------------------------------
105
+ isExportedGo(name) {
106
+ return name.length > 0 && name[0] === name[0].toUpperCase();
107
+ }
108
+ getReceiverType(node) {
109
+ const receiver = node.childForFieldName?.('receiver');
110
+ if (!receiver)
111
+ return undefined;
112
+ for (const child of receiver.namedChildren) {
113
+ if (child.type === 'parameter_declaration') {
114
+ const typeNode = child.childForFieldName?.('type');
115
+ if (typeNode)
116
+ return typeNode.text;
117
+ }
325
118
  }
326
- for (const child of node.children) {
327
- this.collectErrors(child, errors);
119
+ return undefined;
120
+ }
121
+ getReturnType(node) {
122
+ if (node.type !== 'function_declaration')
123
+ return null;
124
+ const result = node.childForFieldName?.('result');
125
+ if (result)
126
+ return result.text.trim();
127
+ return null;
128
+ }
129
+ extractParameters(node, _sourceCode) {
130
+ const params = [];
131
+ const paramList = node.childForFieldName?.('parameters');
132
+ if (!paramList)
133
+ return params;
134
+ for (const child of paramList.namedChildren) {
135
+ if (child.type === 'parameter_declaration') {
136
+ const nameNode = child.childForFieldName?.('name');
137
+ const typeNode = child.childForFieldName?.('type');
138
+ const name = nameNode?.text ?? typeNode?.text ?? child.text;
139
+ params.push({
140
+ name: name || '<unknown>',
141
+ type: typeNode?.text,
142
+ optional: false,
143
+ });
144
+ }
328
145
  }
146
+ return params;
147
+ }
148
+ findNamedChildren(node, type) {
149
+ return node.namedChildren.filter((c) => c.type === type);
329
150
  }
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // Pattern matching + documentation/complexity + build helpers slice
154
+ // ---------------------------------------------------------------------------
155
+ class GoAnalysis extends GoTraversalHelpers {
330
156
  matchesPattern(node, pattern) {
331
157
  const syntaxNode = node.raw;
332
158
  if (pattern.type !== undefined) {
@@ -365,44 +191,14 @@ export class TreeSitterGoAdapter {
365
191
  }
366
192
  return true;
367
193
  }
368
- extractName(node) {
369
- switch (node.type) {
370
- case 'function_declaration':
371
- case 'type_spec':
372
- case 'field_declaration':
373
- case 'method_spec': {
374
- const nameNode = node.childForFieldName?.('name');
375
- if (nameNode)
376
- return nameNode.text;
377
- return null;
378
- }
379
- case 'import_spec': {
380
- const nameNode = node.childForFieldName?.('name');
381
- if (nameNode)
382
- return nameNode.text;
383
- for (const child of node.namedChildren) {
384
- if (child.type === 'interpreted_string_literal' ||
385
- child.type === 'raw_string_literal') {
386
- return child.text.slice(1, -1);
387
- }
388
- }
389
- return null;
390
- }
391
- case 'package_identifier':
392
- case 'identifier':
393
- return node.text;
394
- default:
395
- return null;
396
- }
397
- }
398
194
  extractDocumentation(node) {
399
195
  const parent = node.parent;
400
196
  if (!parent)
401
197
  return null;
402
- const siblings = parent.namedChildren;
198
+ const siblings = parent.children;
403
199
  let myIndex = -1;
404
200
  for (let i = 0; i < siblings.length; i++) {
405
- if (siblings[i] === node) {
201
+ if (siblings[i].equals(node)) {
406
202
  myIndex = i;
407
203
  break;
408
204
  }
@@ -415,7 +211,7 @@ export class TreeSitterGoAdapter {
415
211
  if (sibling.type === 'comment') {
416
212
  comments.unshift(sibling.text);
417
213
  }
418
- else {
214
+ else if (sibling.isNamed) {
419
215
  break;
420
216
  }
421
217
  }
@@ -423,20 +219,6 @@ export class TreeSitterGoAdapter {
423
219
  return null;
424
220
  return this.cleanCommentText(comments.join('\n').trim());
425
221
  }
426
- cleanCommentText(comment) {
427
- if (comment.startsWith('/*')) {
428
- let inner = comment.slice(2, -2);
429
- inner = inner
430
- .split('\n')
431
- .map((line) => line.replace(/^\s*\*\s?/, ''))
432
- .join('\n');
433
- return inner.trim();
434
- }
435
- if (comment.startsWith('//')) {
436
- return comment.replace(/^\/\/\s*/, '').trim();
437
- }
438
- return comment.trim();
439
- }
440
222
  calculateComplexity(node) {
441
223
  let complexity = 1;
442
224
  this.walkRaw(node, (child) => {
@@ -465,15 +247,6 @@ export class TreeSitterGoAdapter {
465
247
  });
466
248
  return complexity;
467
249
  }
468
- getOperator(node) {
469
- for (const child of node.children) {
470
- if (!child.isNamed &&
471
- ['&&', '||', '+', '-', '*', '/', '%', '==', '!='].includes(child.type)) {
472
- return child.type;
473
- }
474
- }
475
- return null;
476
- }
477
250
  // -- Build helpers ---------------------------------------------------------
478
251
  buildFunctionInfo(node, sourceCode) {
479
252
  const name = this.extractName(node);
@@ -495,10 +268,17 @@ export class TreeSitterGoAdapter {
495
268
  jsDoc: jsDoc ?? undefined,
496
269
  };
497
270
  }
498
- buildStructAsClass(_typeSpec, nameNode, typeNode, allFunctions, _sourceCode) {
271
+ buildStructAsClass(_typeSpec, nameNode, typeNode, allFunctions) {
499
272
  const name = nameNode.text;
500
273
  const isExported = this.isExportedGo(name);
501
- const jsDoc = this.extractDocumentation(_typeSpec);
274
+ // A single `type Foo struct { ... }` declaration carries its doc comment
275
+ // as a sibling of the enclosing `type_declaration`; a grouped
276
+ // `type ( ... )` declaration carries it as a sibling of the `type_spec`.
277
+ // Try the `type_spec` level first, then fall back to the `type_declaration`.
278
+ let jsDoc = this.extractDocumentation(_typeSpec);
279
+ if (!jsDoc && _typeSpec.parent?.type === 'type_declaration') {
280
+ jsDoc = this.extractDocumentation(_typeSpec.parent);
281
+ }
502
282
  // Collect fields as properties
503
283
  const properties = [];
504
284
  for (const field of this.findNamedChildren(typeNode, 'field_declaration')) {
@@ -569,72 +349,332 @@ export class TreeSitterGoAdapter {
569
349
  });
570
350
  }
571
351
  }
572
- else if (member.type === 'type_elem') {
573
- const embeddedName = member.childForFieldName?.('name')?.text ?? member.text;
574
- if (embeddedName) {
575
- members.push({
576
- name: embeddedName,
577
- type: 'property',
578
- location: toSourceLocation(member),
579
- });
352
+ else if (member.type === 'type_elem') {
353
+ const embeddedName = member.childForFieldName?.('name')?.text ?? member.text;
354
+ if (embeddedName) {
355
+ members.push({
356
+ name: embeddedName,
357
+ type: 'property',
358
+ location: toSourceLocation(member),
359
+ });
360
+ }
361
+ }
362
+ }
363
+ return {
364
+ name,
365
+ location: toSourceLocation(nameNode),
366
+ members,
367
+ isExported,
368
+ };
369
+ }
370
+ }
371
+ // ---------------------------------------------------------------------------
372
+ // Parsing + AST navigation slice
373
+ // ---------------------------------------------------------------------------
374
+ class GoParserCore extends GoAnalysis {
375
+ name = 'go';
376
+ fileExtensions = ['.go'];
377
+ // -- File detection -------------------------------------------------------
378
+ supportsFile(filePath) {
379
+ return filePath.endsWith('.go');
380
+ }
381
+ // -- Parsing --------------------------------------------------------------
382
+ async parse(filePath, content) {
383
+ const tree = await parseWithRecovery('go', false, content);
384
+ if (!tree)
385
+ throw new Error(`Failed to parse Go file: ${filePath}`);
386
+ const errors = [];
387
+ this.collectErrors(tree.rootNode, errors);
388
+ const root = toASTNode(tree.rootNode, undefined, 'go');
389
+ const ast = {
390
+ root,
391
+ language: 'go',
392
+ filePath,
393
+ errors,
394
+ dispose: () => tree.delete(),
395
+ };
396
+ sourceCodeMap.set(ast, content);
397
+ return ast;
398
+ }
399
+ // -- AST Navigation -------------------------------------------------------
400
+ findNodes(ast, pattern) {
401
+ const results = [];
402
+ this.walk(ast.root, (node) => {
403
+ if (this.matchesPattern(node, pattern)) {
404
+ results.push(node);
405
+ }
406
+ });
407
+ return results;
408
+ }
409
+ getParent(node) {
410
+ return node.parent ?? null;
411
+ }
412
+ getChildren(node) {
413
+ return node.children ?? [];
414
+ }
415
+ getSiblings(node) {
416
+ if (!node.parent?.children)
417
+ return [];
418
+ return node.parent.children.filter((c) => c !== node);
419
+ }
420
+ // -- Node Information -----------------------------------------------------
421
+ getNodeType(node) {
422
+ return node.type;
423
+ }
424
+ getNodeText(node, sourceCode) {
425
+ return sourceCode.slice(node.range[0], node.range[1]);
426
+ }
427
+ getNodeName(node) {
428
+ return this.extractName(node.raw);
429
+ }
430
+ getNodeLocation(node) {
431
+ return node.location;
432
+ }
433
+ }
434
+ // ---------------------------------------------------------------------------
435
+ // Language-specific extraction slice
436
+ // ---------------------------------------------------------------------------
437
+ class GoExtraction extends GoParserCore {
438
+ extractFunctions(ast) {
439
+ const sourceCode = sourceCodeMap.get(ast) ?? '';
440
+ const functions = [];
441
+ this.walk(ast.root, (astNode) => {
442
+ const node = astNode.raw;
443
+ if (node.type === 'function_declaration') {
444
+ const fn = this.buildFunctionInfo(node, sourceCode);
445
+ if (fn)
446
+ functions.push(fn);
447
+ }
448
+ });
449
+ return functions;
450
+ }
451
+ extractClasses(ast) {
452
+ const classes = [];
453
+ // Collect all function_declarations so we can match methods to structs
454
+ const allFunctions = this.extractFunctions(ast);
455
+ this.walk(ast.root, (astNode) => {
456
+ const node = astNode.raw;
457
+ if (node.type === 'type_declaration') {
458
+ const specs = this.findNamedChildren(node, 'type_spec');
459
+ for (const spec of specs) {
460
+ const nameNode = spec.childForFieldName?.('name');
461
+ if (!nameNode)
462
+ continue;
463
+ const typeNode = spec.childForFieldName?.('type');
464
+ if (!typeNode || typeNode.type !== 'struct_type')
465
+ continue;
466
+ const structInfo = this.buildStructAsClass(spec, nameNode, typeNode, allFunctions);
467
+ if (structInfo)
468
+ classes.push(structInfo);
469
+ }
470
+ }
471
+ });
472
+ return classes;
473
+ }
474
+ extractImports(ast) {
475
+ const imports = [];
476
+ this.walk(ast.root, (astNode) => {
477
+ const node = astNode.raw;
478
+ if (node.type === 'import_declaration') {
479
+ for (const spec of node.namedChildren) {
480
+ if (spec.type === 'import_spec') {
481
+ const imp = this.buildImportInfo(spec);
482
+ if (imp)
483
+ imports.push(imp);
484
+ }
485
+ }
486
+ }
487
+ });
488
+ return imports;
489
+ }
490
+ extractExports(ast) {
491
+ const exports = [];
492
+ this.walk(ast.root, (astNode) => {
493
+ const node = astNode.raw;
494
+ if (node.type === 'function_declaration') {
495
+ const name = this.extractName(node);
496
+ if (name && this.isExportedGo(name)) {
497
+ exports.push({
498
+ name,
499
+ location: toSourceLocation(node),
500
+ isDefault: false,
501
+ });
502
+ }
503
+ }
504
+ else if (node.type === 'type_declaration') {
505
+ for (const spec of this.findNamedChildren(node, 'type_spec')) {
506
+ const nameNode = spec.childForFieldName?.('name');
507
+ if (nameNode && this.isExportedGo(nameNode.text)) {
508
+ exports.push({
509
+ name: nameNode.text,
510
+ location: toSourceLocation(nameNode),
511
+ isDefault: false,
512
+ });
513
+ }
580
514
  }
581
515
  }
582
- }
583
- return {
584
- name,
585
- location: toSourceLocation(nameNode),
586
- members,
587
- isExported,
588
- };
589
- }
590
- extractParameters(node, _sourceCode) {
591
- const params = [];
592
- const paramList = node.childForFieldName?.('parameters');
593
- if (!paramList)
594
- return params;
595
- for (const child of paramList.namedChildren) {
596
- if (child.type === 'parameter_declaration') {
597
- const nameNode = child.childForFieldName?.('name');
598
- const typeNode = child.childForFieldName?.('type');
599
- const name = nameNode?.text ?? typeNode?.text ?? child.text;
600
- params.push({
601
- name: name || '<unknown>',
602
- type: typeNode?.text,
603
- optional: false,
604
- });
516
+ else if (node.type === 'var_declaration') {
517
+ for (const spec of this.findNamedChildren(node, 'var_spec')) {
518
+ const nameNode = spec.childForFieldName?.('name');
519
+ if (nameNode && this.isExportedGo(nameNode.text)) {
520
+ exports.push({
521
+ name: nameNode.text,
522
+ location: toSourceLocation(nameNode),
523
+ isDefault: false,
524
+ });
525
+ }
526
+ }
605
527
  }
528
+ });
529
+ return exports;
530
+ }
531
+ }
532
+ // ---------------------------------------------------------------------------
533
+ // Predicates + advanced-features slice
534
+ // ---------------------------------------------------------------------------
535
+ class GoPredicates extends GoExtraction {
536
+ isClass(node) {
537
+ const n = node.raw;
538
+ if (n.type === 'type_spec') {
539
+ const typeNode = n.childForFieldName?.('type');
540
+ return typeNode?.type === 'struct_type';
606
541
  }
607
- return params;
542
+ return n.type === 'struct_type';
608
543
  }
609
- // -- Go-specific helpers ---------------------------------------------------
610
- isExportedGo(name) {
611
- return name.length > 0 && name[0] === name[0].toUpperCase();
544
+ isFunction(node) {
545
+ return node.raw.type === 'function_declaration';
612
546
  }
613
- getReceiverType(node) {
614
- const receiver = node.childForFieldName?.('receiver');
615
- if (!receiver)
616
- return undefined;
617
- for (const child of receiver.namedChildren) {
618
- if (child.type === 'parameter_declaration') {
619
- const typeNode = child.childForFieldName?.('type');
620
- if (typeNode)
621
- return typeNode.text;
622
- }
547
+ isMethod(node) {
548
+ const n = node.raw;
549
+ if (n.type !== 'function_declaration')
550
+ return false;
551
+ return n.childForFieldName?.('receiver') != null;
552
+ }
553
+ isInterface(node) {
554
+ const n = node.raw;
555
+ if (n.type === 'type_spec') {
556
+ const typeNode = n.childForFieldName?.('type');
557
+ return typeNode?.type === 'interface_type';
623
558
  }
624
- return undefined;
559
+ return n.type === 'interface_type';
625
560
  }
626
- getReturnType(node) {
627
- if (node.type !== 'function_declaration')
628
- return null;
629
- const result = node.childForFieldName?.('result');
630
- if (result)
631
- return result.text.trim();
561
+ isImport(node) {
562
+ return node.raw.type === 'import_declaration';
563
+ }
564
+ isExport(node) {
565
+ return false; // Go has no export keyword
566
+ }
567
+ isLoop(node) {
568
+ return node.raw.type === 'for_statement';
569
+ }
570
+ isConditional(node) {
571
+ const t = node.raw.type;
572
+ return (t === 'if_statement' ||
573
+ t === 'switch_statement' ||
574
+ t === 'expression_switch_statement' ||
575
+ t === 'type_switch_statement');
576
+ }
577
+ isVariableDeclaration(node) {
578
+ const t = node.raw.type;
579
+ return (t === 'var_declaration' ||
580
+ t === 'short_var_declaration' ||
581
+ t === 'var_spec' ||
582
+ t === 'const_declaration');
583
+ }
584
+ // -- Advanced Features ----------------------------------------------------
585
+ getTypeInfo(node) {
586
+ const n = node.raw;
587
+ if (n.type === 'function_declaration') {
588
+ const result = n.childForFieldName?.('result');
589
+ if (result)
590
+ return result.text.trim();
591
+ }
592
+ if (n.type === 'var_spec' || n.type === 'const_spec') {
593
+ const typeNode = n.childForFieldName?.('type');
594
+ if (typeNode)
595
+ return typeNode.text.trim();
596
+ }
597
+ if (n.type === 'field_declaration') {
598
+ const typeNode = n.childForFieldName?.('type');
599
+ if (typeNode)
600
+ return typeNode.text.trim();
601
+ }
632
602
  return null;
633
603
  }
634
- // -- Tree traversal helpers ------------------------------------------------
635
- findNamedChildren(node, type) {
636
- return node.namedChildren.filter((c) => c.type === type);
604
+ getDocumentation(node) {
605
+ return this.extractDocumentation(node.raw);
606
+ }
607
+ getComplexity(node) {
608
+ return this.calculateComplexity(node.raw);
609
+ }
610
+ }
611
+ // ---------------------------------------------------------------------------
612
+ // Optional capabilities slice
613
+ // ---------------------------------------------------------------------------
614
+ class GoOptionalCapabilities extends GoPredicates {
615
+ extractInterfaces(ast) {
616
+ const interfaces = [];
617
+ this.walk(ast.root, (astNode) => {
618
+ const node = astNode.raw;
619
+ if (node.type === 'type_declaration') {
620
+ const specs = this.findNamedChildren(node, 'type_spec');
621
+ for (const spec of specs) {
622
+ const nameNode = spec.childForFieldName?.('name');
623
+ const typeNode = spec.childForFieldName?.('type');
624
+ if (nameNode && typeNode?.type === 'interface_type') {
625
+ const ifaceInfo = this.buildInterfaceInfo(nameNode, typeNode);
626
+ if (ifaceInfo)
627
+ interfaces.push(ifaceInfo);
628
+ }
629
+ }
630
+ }
631
+ });
632
+ return interfaces;
633
+ }
634
+ // -- Optional: Raw imports ------------------------------------------------
635
+ extractRawImports(_filePath, content) {
636
+ const results = [];
637
+ const parser = getParser('go');
638
+ const tree = parser.parse(content);
639
+ if (!tree)
640
+ return results;
641
+ this.walkRaw(tree.rootNode, (node) => {
642
+ if (node.type === 'import_declaration') {
643
+ for (const spec of node.namedChildren) {
644
+ if (spec.type === 'import_spec') {
645
+ for (const child of spec.namedChildren) {
646
+ if (child.type === 'interpreted_string_literal' ||
647
+ child.type === 'raw_string_literal') {
648
+ results.push({
649
+ moduleSpecifier: child.text.slice(1, -1),
650
+ isStatic: true,
651
+ isDynamic: false,
652
+ isRequire: false,
653
+ line: child.startPosition.row,
654
+ });
655
+ }
656
+ }
657
+ }
658
+ }
659
+ }
660
+ });
661
+ return results;
662
+ }
663
+ // -- Optional: Exported symbols -------------------------------------------
664
+ extractExportedSymbols(ast) {
665
+ return this.extractExports(ast).map((e) => ({
666
+ name: e.name,
667
+ line: e.location.start.line,
668
+ }));
637
669
  }
670
+ }
671
+ // ---------------------------------------------------------------------------
672
+ // Adapter (composes the slices above)
673
+ // ---------------------------------------------------------------------------
674
+ /**
675
+ * Tree sitter go adapter.
676
+ */
677
+ export class TreeSitterGoAdapter extends GoOptionalCapabilities {
638
678
  // -- String Construction Capabilities (v3.4.7) ----------------------------
639
679
  /**
640
680
  * Returns true when the node is a dynamically-constructed string in Go:
@@ -642,6 +682,8 @@ export class TreeSitterGoAdapter {
642
682
  * - binary_expression with + operator (string concatenation)
643
683
  * Plain string literals (interpreted_string_literal, raw_string_literal)
644
684
  * are never dynamic.
685
+ * @param node
686
+ * @returns
645
687
  */
646
688
  isDynamicStringConstruction(node) {
647
689
  const raw = node.raw;
@@ -653,22 +695,9 @@ export class TreeSitterGoAdapter {
653
695
  // fmt.Sprintf("format", args...) — dynamic
654
696
  // strings.Join(parts, "sep") — dynamic
655
697
  // fmt.Sprint(args...) — dynamic
656
- const funcText = func.text;
657
- if (funcText === 'fmt.Sprintf' || funcText === 'fmt.Sprintf' ||
658
- funcText === 'fmt.Sprint' || funcText === 'fmt.Sprintln' ||
659
- funcText === 'fmt.Appendf' || funcText === 'fmt.Appendln' ||
660
- funcText === 'fmt.Append' || funcText === 'fmt.Errorf' ||
661
- funcText === 'fmt.Fprintf' || funcText === 'fmt.Fprintln' ||
662
- funcText === 'fmt.Fprint' || funcText === 'strings.Join' ||
663
- funcText === 'fmt.Scanf') {
698
+ if (isDynamicFormatFunction(func.text)) {
664
699
  return true;
665
700
  }
666
- // Also check for selector_expression patterns: pkg.Symbol()
667
- const selectorMatch = /^(.*?\.)?(Sprintf|Sprintf|Sprint|Sprintln|Join|Appendf|Errorf|Fprintf)$/.test(funcText);
668
- if (selectorMatch) {
669
- // Verify it's actually fmt.* or strings.*
670
- return funcText.startsWith('fmt.') || funcText.startsWith('strings.');
671
- }
672
701
  // Recurse into arguments: query(fmt.Sprintf(...)) where the outer
673
702
  // call isn't itself a format function but passes a dynamic argument.
674
703
  for (const child of node.children ?? []) {
@@ -699,74 +728,80 @@ export class TreeSitterGoAdapter {
699
728
  * - For fmt.Sprintf: args after the format string are dynamic
700
729
  * - For binary +: non-literal operands are dynamic
701
730
  * - For strings.Join: the parts slice is dynamic
731
+ * @param node
732
+ * @param sourceCode
733
+ * @returns
702
734
  */
703
735
  getDynamicParts(node, sourceCode) {
704
736
  const raw = node.raw;
705
737
  const type = raw.type;
706
- const parts = [];
707
738
  // For call_expressions that aren't known fmt/strings functions,
708
739
  // walk into the first argument that is itself a dynamic string construction.
709
740
  if (type === 'call_expression') {
710
741
  const funcText = raw.childForFieldName?.('function')?.text ?? '';
711
742
  const isKnownFormatter = /^(fmt\.|strings\.)(Sprintf|Fprintf|Errorf|Appendf|Sprint|Sprintln|Append|Appendln|Join|Scanf|Fprint|Fprintln|Sscanf)$/u.test(funcText);
712
743
  if (!isKnownFormatter) {
713
- for (const child of node.children ?? []) {
714
- if (child.raw.type === 'argument_list') {
715
- for (const arg of child.children ?? []) {
716
- const argType = arg.raw.type;
717
- if (argType === '(' || argType === ')' || argType === ',')
718
- continue;
719
- if (this.isDynamicStringConstruction(arg)) {
720
- return this.getDynamicParts(arg, sourceCode);
721
- }
722
- }
723
- }
724
- }
725
- }
726
- }
727
- if (type === 'call_expression') {
728
- const argsNode = raw.childForFieldName?.('arguments');
729
- if (!argsNode)
730
- return parts;
731
- const namedChildren = argsNode.namedChildren;
732
- // fmt.Sprintf("format", arg1, arg2...): skip format string (first arg)
733
- // strings.Join(parts, "sep"): first arg is dynamic
734
- const funcText = raw.childForFieldName?.('function')?.text ?? '';
735
- const isFormatFunc = /^fmt\.(Sprintf|Fprintf|Errorf|Appendf)$/u.test(funcText);
736
- const startIdx = isFormatFunc ? 1 : 0;
737
- for (let i = startIdx; i < namedChildren.length; i++) {
738
- const arg = namedChildren[i];
739
- const argType = arg.type;
740
- // Skip string literals (they're the separator or static parts)
741
- if (argType === 'interpreted_string_literal' || argType === 'raw_string_literal')
742
- continue;
743
- const text = sourceCode.slice(arg.startIndex, arg.endIndex);
744
- const isId = /^[\p{L}_][\p{L}\p{N}_]*$/u.test(text.trim());
745
- const idNode = isId ? {
746
- type: argType,
747
- range: [arg.startIndex, arg.endIndex],
748
- location: {
749
- start: { line: arg.startPosition.row, column: arg.startPosition.column },
750
- end: { line: arg.endPosition.row, column: arg.endPosition.column },
751
- },
752
- raw: arg,
753
- } : undefined;
754
- parts.push({ text: text.trim(), isIdentifier: isId, node: idNode });
744
+ const arg = this.findFirstDynamicArgument(node);
745
+ if (arg)
746
+ return this.getDynamicParts(arg, sourceCode);
755
747
  }
756
- return parts;
748
+ return this.extractCallDynamicParts(raw, sourceCode);
757
749
  }
758
750
  if (type === 'binary_expression') {
759
- for (const child of node.children ?? []) {
760
- const childType = child.raw.type;
761
- if (childType === 'interpreted_string_literal' ||
762
- childType === 'raw_string_literal' ||
763
- childType === '+')
751
+ return extractBinaryDynamicParts(node, sourceCode);
752
+ }
753
+ return [];
754
+ }
755
+ /**
756
+ * Finds the first argument of a call_expression that is itself a dynamic
757
+ * string construction, or null if none exists.
758
+ */
759
+ findFirstDynamicArgument(node) {
760
+ for (const child of node.children ?? []) {
761
+ if (child.raw.type !== 'argument_list')
762
+ continue;
763
+ for (const arg of child.children ?? []) {
764
+ const argType = arg.raw.type;
765
+ if (argType === '(' || argType === ')' || argType === ',')
764
766
  continue;
765
- const text = sourceCode.slice(child.range[0], child.range[1]);
766
- const isId = /^[\p{L}_][\p{L}\p{N}_]*$/u.test(text.trim());
767
- parts.push({ text: text.trim(), isIdentifier: isId, node: isId ? child : undefined });
767
+ if (this.isDynamicStringConstruction(arg))
768
+ return arg;
768
769
  }
770
+ }
771
+ return null;
772
+ }
773
+ /**
774
+ * Extracts dynamic parts from a call_expression's argument list.
775
+ * fmt.Sprintf("format", arg1...): skips the format string (first arg).
776
+ * strings.Join(parts, "sep"): first arg is dynamic.
777
+ */
778
+ extractCallDynamicParts(raw, sourceCode) {
779
+ const parts = [];
780
+ const argsNode = raw.childForFieldName?.('arguments');
781
+ if (!argsNode)
769
782
  return parts;
783
+ const namedChildren = argsNode.namedChildren;
784
+ const funcText = raw.childForFieldName?.('function')?.text ?? '';
785
+ const isFormatFunc = /^fmt\.(Sprintf|Fprintf|Errorf|Appendf)$/u.test(funcText);
786
+ const startIdx = isFormatFunc ? 1 : 0;
787
+ for (let i = startIdx; i < namedChildren.length; i++) {
788
+ const arg = namedChildren[i];
789
+ const argType = arg.type;
790
+ // Skip string literals (they're the separator or static parts)
791
+ if (argType === 'interpreted_string_literal' || argType === 'raw_string_literal')
792
+ continue;
793
+ const text = sourceCode.slice(arg.startIndex, arg.endIndex);
794
+ const isId = /^[\p{L}_][\p{L}\p{N}_]*$/u.test(text.trim());
795
+ const idNode = isId ? {
796
+ type: argType,
797
+ range: [arg.startIndex, arg.endIndex],
798
+ location: {
799
+ start: { line: arg.startPosition.row, column: arg.startPosition.column },
800
+ end: { line: arg.endPosition.row, column: arg.endPosition.column },
801
+ },
802
+ raw: arg,
803
+ } : undefined;
804
+ parts.push({ text: text.trim(), isIdentifier: isId, node: idNode });
770
805
  }
771
806
  return parts;
772
807
  }
@@ -774,6 +809,10 @@ export class TreeSitterGoAdapter {
774
809
  * Resolves a Go local constant declaration for an identifier node.
775
810
  * Searches within the enclosing function scope for var/const/:=
776
811
  * declarations. Returns null for unresolvable identifiers.
812
+ * @param ast
813
+ * @param identifierNode
814
+ * @param sourceCode
815
+ * @returns
777
816
  */
778
817
  resolveLocalConstant(identifierNode, ast, sourceCode) {
779
818
  const idName = sourceCode.slice(identifierNode.range[0], identifierNode.range[1]).trim();
@@ -880,4 +919,44 @@ export class TreeSitterGoAdapter {
880
919
  return found;
881
920
  }
882
921
  }
922
+ /**
923
+ * Whether a call_expression's function name denotes a dynamic string formatter
924
+ * (fmt.Sprint*, fmt.Errorf, strings.Join, etc.).
925
+ */
926
+ function isDynamicFormatFunction(funcText) {
927
+ if (funcText === 'fmt.Sprintf' || funcText === 'fmt.Sprint' ||
928
+ funcText === 'fmt.Sprintln' || funcText === 'fmt.Appendf' ||
929
+ funcText === 'fmt.Appendln' || funcText === 'fmt.Append' ||
930
+ funcText === 'fmt.Errorf' || funcText === 'fmt.Fprintf' ||
931
+ funcText === 'fmt.Fprintln' || funcText === 'fmt.Fprint' ||
932
+ funcText === 'strings.Join' || funcText === 'fmt.Scanf') {
933
+ return true;
934
+ }
935
+ // Also check for selector_expression patterns: pkg.Symbol()
936
+ const selectorMatch = /^(.*?\.)?(Sprintf|Sprint|Sprintln|Join|Appendf|Errorf|Fprintf)$/.test(funcText);
937
+ if (selectorMatch) {
938
+ // Verify it's actually fmt.* or strings.*
939
+ return funcText.startsWith('fmt.') || funcText.startsWith('strings.');
940
+ }
941
+ return false;
942
+ }
943
+ /**
944
+ * Extracts dynamic parts from a Go binary string-concatenation expression
945
+ * (a + b + c). Non-literal operands are dynamic; literals and the `+` operator
946
+ * are skipped.
947
+ */
948
+ function extractBinaryDynamicParts(node, sourceCode) {
949
+ const parts = [];
950
+ for (const child of node.children ?? []) {
951
+ const childType = child.raw.type;
952
+ if (childType === 'interpreted_string_literal' ||
953
+ childType === 'raw_string_literal' ||
954
+ childType === '+')
955
+ continue;
956
+ const text = sourceCode.slice(child.range[0], child.range[1]);
957
+ const isId = /^[\p{L}_][\p{L}\p{N}_]*$/u.test(text.trim());
958
+ parts.push({ text: text.trim(), isIdentifier: isId, node: isId ? child : undefined });
959
+ }
960
+ return parts;
961
+ }
883
962
  //# sourceMappingURL=GoAdapter.js.map