gitnexus 1.6.12-rc.2 → 1.6.12-rc.21

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 (188) hide show
  1. package/dist/_shared/graph/types.d.ts +1 -1
  2. package/dist/_shared/graph/types.d.ts.map +1 -1
  3. package/dist/_shared/language-detection.d.ts.map +1 -1
  4. package/dist/_shared/language-detection.js +2 -0
  5. package/dist/_shared/language-detection.js.map +1 -1
  6. package/dist/_shared/languages.d.ts +1 -0
  7. package/dist/_shared/languages.d.ts.map +1 -1
  8. package/dist/_shared/languages.js +1 -0
  9. package/dist/_shared/languages.js.map +1 -1
  10. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  11. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  12. package/dist/_shared/lbug/schema-constants.js +2 -0
  13. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  14. package/dist/_shared/scope-resolution/language-classification.d.ts +2 -1
  15. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/language-classification.js +3 -1
  17. package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
  18. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +3 -3
  19. package/dist/_shared/scope-resolution/registries/class-registry.js +3 -3
  20. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
  21. package/dist/_shared/scope-resolution/registries/context.js +2 -0
  22. package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
  23. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/resolve-type-ref.js +2 -0
  25. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  26. package/dist/cli/analyze-config.d.ts +2 -2
  27. package/dist/cli/analyze-config.js +10 -33
  28. package/dist/cli/doctor.js +6 -4
  29. package/dist/cli/eval-server.js +13 -1
  30. package/dist/cli/i18n/en.d.ts +4 -2
  31. package/dist/cli/i18n/en.js +4 -2
  32. package/dist/cli/i18n/resources.d.ts +6 -2
  33. package/dist/cli/i18n/zh-CN.d.ts +2 -0
  34. package/dist/cli/i18n/zh-CN.js +4 -2
  35. package/dist/core/analysis-feature-registry.d.ts +1 -1
  36. package/dist/core/analysis-feature-registry.js +2 -0
  37. package/dist/core/auto-sync/starter.js +2 -2
  38. package/dist/core/embeddings/ast-utils.js +8 -3
  39. package/dist/core/embeddings/chunker.js +67 -10
  40. package/dist/core/embeddings/embedding-pipeline.d.ts +1 -1
  41. package/dist/core/embeddings/embedding-pipeline.js +1 -1
  42. package/dist/core/embeddings/structural-extractor.js +2 -2
  43. package/dist/core/embeddings/types.d.ts +4 -2
  44. package/dist/core/embeddings/types.js +18 -0
  45. package/dist/core/git-ref.d.ts +22 -0
  46. package/dist/core/git-ref.js +114 -0
  47. package/dist/core/git-staleness.js +14 -0
  48. package/dist/core/group/extractors/graphql-extractor.d.ts +1 -1
  49. package/dist/core/group/extractors/graphql-extractor.js +149 -13
  50. package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
  51. package/dist/core/group/extractors/manifest-extractor.js +2 -2
  52. package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +5 -8
  53. package/dist/core/ingestion/content-language-classification.d.ts +10 -0
  54. package/dist/core/ingestion/content-language-classification.js +30 -0
  55. package/dist/core/ingestion/filesystem-walker.d.ts +1 -0
  56. package/dist/core/ingestion/filesystem-walker.js +1 -1
  57. package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +3 -2
  58. package/dist/core/ingestion/language-config.d.ts +82 -13
  59. package/dist/core/ingestion/language-config.js +217 -13
  60. package/dist/core/ingestion/language-provider.d.ts +88 -1
  61. package/dist/core/ingestion/languages/index.d.ts +7 -0
  62. package/dist/core/ingestion/languages/index.js +30 -1
  63. package/dist/core/ingestion/languages/objective-c/analysis-features.d.ts +10 -0
  64. package/dist/core/ingestion/languages/objective-c/analysis-features.js +19 -0
  65. package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.d.ts +13 -0
  66. package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.js +124 -0
  67. package/dist/core/ingestion/languages/objective-c/facts.d.ts +148 -0
  68. package/dist/core/ingestion/languages/objective-c/facts.js +1150 -0
  69. package/dist/core/ingestion/languages/objective-c/import-target.d.ts +11 -0
  70. package/dist/core/ingestion/languages/objective-c/import-target.js +122 -0
  71. package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.d.ts +24 -0
  72. package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.js +261 -0
  73. package/dist/core/ingestion/languages/objective-c/resolution-config.d.ts +41 -0
  74. package/dist/core/ingestion/languages/objective-c/resolution-config.js +282 -0
  75. package/dist/core/ingestion/languages/objective-c/scope-resolver.d.ts +2 -0
  76. package/dist/core/ingestion/languages/objective-c/scope-resolver.js +503 -0
  77. package/dist/core/ingestion/languages/objective-c.d.ts +2 -0
  78. package/dist/core/ingestion/languages/objective-c.js +300 -0
  79. package/dist/core/ingestion/languages/typescript/query.d.ts +31 -0
  80. package/dist/core/ingestion/languages/typescript/query.js +7 -1
  81. package/dist/core/ingestion/languages/typescript/tsconfig.js +3 -2
  82. package/dist/core/ingestion/languages/typescript.js +1 -1
  83. package/dist/core/ingestion/languages/zig/query.d.ts +26 -0
  84. package/dist/core/ingestion/languages/zig/query.js +61 -1
  85. package/dist/core/ingestion/languages/zig/range-binding.d.ts +8 -0
  86. package/dist/core/ingestion/languages/zig/range-binding.js +24 -0
  87. package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +3 -2
  88. package/dist/core/ingestion/languages/zig/scope-resolver.js +18 -5
  89. package/dist/core/ingestion/languages/zig/this-alias-bindings.d.ts +58 -0
  90. package/dist/core/ingestion/languages/zig/this-alias-bindings.js +176 -0
  91. package/dist/core/ingestion/languages/zig/workspace-static-gating.d.ts +8 -0
  92. package/dist/core/ingestion/languages/zig/workspace-static-gating.js +82 -0
  93. package/dist/core/ingestion/model/registration-table.d.ts +3 -3
  94. package/dist/core/ingestion/model/registration-table.js +9 -5
  95. package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
  96. package/dist/core/ingestion/model/symbol-table.js +2 -0
  97. package/dist/core/ingestion/model/type-registry.d.ts +1 -1
  98. package/dist/core/ingestion/parsing-processor.d.ts +22 -6
  99. package/dist/core/ingestion/parsing-processor.js +39 -20
  100. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +9 -1
  101. package/dist/core/ingestion/pipeline-phases/parse-impl.js +307 -96
  102. package/dist/core/ingestion/pipeline-phases/parse-round-budget.d.ts +42 -0
  103. package/dist/core/ingestion/pipeline-phases/parse-round-budget.js +50 -0
  104. package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -1
  105. package/dist/core/ingestion/scope-extractor.js +4 -0
  106. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +13 -0
  107. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +2 -0
  108. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +2 -0
  109. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +78 -5
  110. package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +262 -7
  111. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +5 -2
  112. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  113. package/dist/core/ingestion/scope-resolution/pipeline/run.js +10 -1
  114. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +39 -3
  115. package/dist/core/ingestion/scope-resolution/scope/walkers.js +82 -3
  116. package/dist/core/ingestion/scope-resolution/value-ref-edges.d.ts +14 -0
  117. package/dist/core/ingestion/scope-resolution/value-ref-edges.js +14 -0
  118. package/dist/core/ingestion/tree-sitter-queries.js +2 -0
  119. package/dist/core/ingestion/utils/symbol-labels.js +2 -0
  120. package/dist/core/ingestion/workers/parse-worker.d.ts +1 -1
  121. package/dist/core/ingestion/workers/parse-worker.js +63 -3
  122. package/dist/core/ingestion/workers/worker-pool.d.ts +56 -2
  123. package/dist/core/ingestion/workers/worker-pool.js +117 -26
  124. package/dist/core/lbug/csv-generator.js +2 -0
  125. package/dist/core/lbug/schema.d.ts +3 -1
  126. package/dist/core/lbug/schema.js +27 -0
  127. package/dist/core/search/fts-indexes.js +13 -4
  128. package/dist/core/search/fts-schema.js +2 -0
  129. package/dist/core/tree-sitter/parser-loader.js +10 -0
  130. package/dist/core/tree-sitter/vendored-grammars.d.ts +1 -1
  131. package/dist/core/tree-sitter/vendored-grammars.js +2 -1
  132. package/dist/mcp/local/local-backend.d.ts +33 -0
  133. package/dist/mcp/local/local-backend.js +166 -5
  134. package/dist/mcp/tools.js +6 -4
  135. package/dist/server/analyze-job.d.ts +19 -1
  136. package/dist/server/analyze-job.js +14 -3
  137. package/dist/server/analyze-launch.d.ts +14 -0
  138. package/dist/server/analyze-launch.js +59 -14
  139. package/dist/server/analyze-worker-ipc.d.ts +6 -5
  140. package/dist/server/analyze-worker-ipc.js +4 -0
  141. package/dist/server/api.js +68 -21
  142. package/dist/server/git-clone.d.ts +38 -5
  143. package/dist/server/git-clone.js +216 -38
  144. package/dist/server/repo-projection.d.ts +88 -0
  145. package/dist/server/repo-projection.js +53 -0
  146. package/dist/storage/file-lock.js +5 -3
  147. package/dist/storage/parse-cache.js +32 -1
  148. package/dist/storage/parsedfile-store.js +53 -1
  149. package/dist/storage/v8-sidecar.d.ts +11 -0
  150. package/dist/storage/v8-sidecar.js +15 -7
  151. package/dist/utils/process-identity.d.ts +13 -0
  152. package/dist/utils/process-identity.js +18 -0
  153. package/package.json +1 -1
  154. package/scripts/build-tree-sitter-grammars.cjs +4 -3
  155. package/vendor/tree-sitter-objc/LICENSE +21 -0
  156. package/vendor/tree-sitter-objc/README.md +18 -0
  157. package/vendor/tree-sitter-objc/binding.gyp +35 -0
  158. package/vendor/tree-sitter-objc/bindings/node/binding.cc +19 -0
  159. package/vendor/tree-sitter-objc/bindings/node/binding_test.js +9 -0
  160. package/vendor/tree-sitter-objc/bindings/node/index.d.ts +27 -0
  161. package/vendor/tree-sitter-objc/bindings/node/index.js +11 -0
  162. package/vendor/tree-sitter-objc/grammar.js +1286 -0
  163. package/vendor/tree-sitter-objc/package.json +56 -0
  164. package/vendor/tree-sitter-objc/prebuilds/SHA256SUMS +6 -0
  165. package/vendor/tree-sitter-objc/prebuilds/darwin-arm64/tree-sitter-objc.node +0 -0
  166. package/vendor/tree-sitter-objc/prebuilds/darwin-x64/tree-sitter-objc.node +0 -0
  167. package/vendor/tree-sitter-objc/prebuilds/linux-arm64/tree-sitter-objc.node +0 -0
  168. package/vendor/tree-sitter-objc/prebuilds/linux-x64/tree-sitter-objc.node +0 -0
  169. package/vendor/tree-sitter-objc/prebuilds/win32-arm64/tree-sitter-objc.node +0 -0
  170. package/vendor/tree-sitter-objc/prebuilds/win32-x64/tree-sitter-objc.node +0 -0
  171. package/vendor/tree-sitter-objc/queries/folds.scm +20 -0
  172. package/vendor/tree-sitter-objc/queries/highlights.scm +216 -0
  173. package/vendor/tree-sitter-objc/queries/indents.scm +1 -0
  174. package/vendor/tree-sitter-objc/queries/injections.scm +10 -0
  175. package/vendor/tree-sitter-objc/queries/locals.scm +1 -0
  176. package/vendor/tree-sitter-objc/src/grammar.json +16292 -0
  177. package/vendor/tree-sitter-objc/src/node-types.json +7524 -0
  178. package/vendor/tree-sitter-objc/src/parser.c +684023 -0
  179. package/vendor/tree-sitter-objc/src/tree_sitter/alloc.h +54 -0
  180. package/vendor/tree-sitter-objc/src/tree_sitter/array.h +291 -0
  181. package/vendor/tree-sitter-objc/src/tree_sitter/parser.h +266 -0
  182. package/vendor/tree-sitter-objc/tree-sitter-objc.wasm +0 -0
  183. package/vendor/tree-sitter-objc/tree-sitter.json +38 -0
  184. package/web/assets/{agent-BMRqXorA.js → agent-C2n33ANd.js} +1 -1
  185. package/web/assets/{index-Dj3vQOHK.js → index-CHJoW_P1.js} +18 -18
  186. package/web/assets/src-jc8Ffy3T.js +1 -0
  187. package/web/index.html +2 -2
  188. package/web/assets/src-Df5C1Nz4.js +0 -1
@@ -0,0 +1,1150 @@
1
+ import { SupportedLanguages } from '../../../../_shared/index.js';
2
+ import { generateId } from '../../../../lib/utils.js';
3
+ import { nodeToCapture, walkNamedTree } from '../../utils/ast-helpers.js';
4
+ export const OBJECTIVE_C_PROVIDER_VERSION = '0.1.8';
5
+ export const OBJECTIVE_C_GRAMMAR_PACKAGE = 'tree-sitter-objc';
6
+ export const OBJECTIVE_C_GRAMMAR_VERSION = '3.0.2';
7
+ const factsByFile = new Map();
8
+ export function setObjectiveCFileFacts(facts) {
9
+ factsByFile.set(facts.filePath, facts);
10
+ }
11
+ export function getObjectiveCFileFacts(filePath) {
12
+ return factsByFile.get(filePath);
13
+ }
14
+ /** Drop process-local facts before a new workspace pass (Java/Kotlin analog). */
15
+ export function clearObjectiveCFileFacts() {
16
+ factsByFile.clear();
17
+ }
18
+ export function collectObjectiveCCaptureSideChannel(filePath) {
19
+ const facts = factsByFile.get(filePath);
20
+ return facts === undefined ? undefined : { kind: 'objective-c', facts };
21
+ }
22
+ export function applyObjectiveCCaptureSideChannel(parsed) {
23
+ const payload = parsed.captureSideChannel;
24
+ if (!isObjectiveCSideChannel(payload))
25
+ return;
26
+ factsByFile.set(payload.facts.filePath, payload.facts);
27
+ }
28
+ export function objectiveCFactsFromParsedFiles(parsedFiles) {
29
+ const facts = [];
30
+ const seen = new Set();
31
+ for (const parsed of parsedFiles) {
32
+ const payload = parsed.captureSideChannel;
33
+ const fromPayload = isObjectiveCSideChannel(payload) ? payload.facts : undefined;
34
+ const fact = fromPayload ?? factsByFile.get(parsed.filePath);
35
+ if (fact === undefined || seen.has(fact.filePath))
36
+ continue;
37
+ seen.add(fact.filePath);
38
+ facts.push(fact);
39
+ }
40
+ return facts;
41
+ }
42
+ function isObjectiveCSideChannel(value) {
43
+ if (value === null || typeof value !== 'object')
44
+ return false;
45
+ const record = value;
46
+ return record.kind === 'objective-c' && record.facts !== null && typeof record.facts === 'object';
47
+ }
48
+ export const objcClassQualifiedName = (name) => `objc:class:${name}`;
49
+ export const objcProtocolQualifiedName = (name) => `objc:protocol:${name}`;
50
+ export const objcCategoryQualifiedName = (hostClass, categoryName) => `objc:category:${hostClass}:${categoryName}`;
51
+ export const objcMethodQualifiedName = (ownerQualifiedName, methodKind, selector) => `objc:method:${ownerQualifiedName}:${methodKind}:${selector}`;
52
+ export const objcPropertyQualifiedName = (ownerQualifiedName, name) => `objc:property:${ownerQualifiedName}:${name}`;
53
+ export const objcIvarQualifiedName = (ownerQualifiedName, name) => `objc:ivar:${ownerQualifiedName}:${name}`;
54
+ export const objcFunctionQualifiedName = (name, linkage = 'external', filePath) => linkage === 'internal' && filePath !== undefined
55
+ ? `objc:function:static:${filePath}:${name}`
56
+ : `objc:function:${name}`;
57
+ /** True for C functions this provider models as translation-unit local. */
58
+ export function isInternalObjectiveCFunctionDef(def) {
59
+ const qualifiedName = def.qualifiedName ?? '';
60
+ const nodeId = def.nodeId ?? '';
61
+ return (qualifiedName.startsWith('objc:function:static:') || nodeId.includes('objc:function:static:'));
62
+ }
63
+ export const objcUnresolvedMessageQualifiedName = (filePath, startLine, startCol, selector) => `objc:unresolved:${filePath}:${startLine}:${startCol}:${selector}`;
64
+ const graphNodeId = (label, qualifiedName) => generateId(label, qualifiedName);
65
+ function ownerLabel(kind) {
66
+ return kind === 'class' ? 'Class' : kind === 'protocol' ? 'Protocol' : 'Category';
67
+ }
68
+ const range = (node) => ({
69
+ startLine: node.startPosition.row,
70
+ endLine: node.endPosition.row,
71
+ });
72
+ function directNamedChildren(node) {
73
+ const out = [];
74
+ for (let i = 0; i < node.namedChildCount; i++) {
75
+ const child = node.namedChild(i);
76
+ if (child !== null)
77
+ out.push(child);
78
+ }
79
+ return out;
80
+ }
81
+ function directChildren(node) {
82
+ const out = [];
83
+ for (let i = 0; i < node.childCount; i++) {
84
+ const child = node.child(i);
85
+ if (child !== null)
86
+ out.push(child);
87
+ }
88
+ return out;
89
+ }
90
+ function directIdentifiers(node) {
91
+ return directNamedChildren(node).filter((child) => child.type === 'identifier');
92
+ }
93
+ function logicalTopLevelNodes(root) {
94
+ const nodes = [];
95
+ const visit = (node) => {
96
+ for (const child of directNamedChildren(node)) {
97
+ if (child.type === 'preproc_if' ||
98
+ child.type === 'preproc_ifdef' ||
99
+ child.type === 'preproc_else' ||
100
+ child.type === 'preproc_elif') {
101
+ visit(child);
102
+ }
103
+ else {
104
+ nodes.push(child);
105
+ }
106
+ }
107
+ };
108
+ visit(root);
109
+ return nodes;
110
+ }
111
+ function containerMemberNodes(containerNode) {
112
+ const members = [];
113
+ const visit = (node) => {
114
+ for (const child of directNamedChildren(node)) {
115
+ if (child.type === 'qualified_protocol_interface_declaration') {
116
+ visit(child);
117
+ }
118
+ else {
119
+ members.push(child);
120
+ }
121
+ }
122
+ };
123
+ visit(containerNode);
124
+ return members;
125
+ }
126
+ function stripQuotes(raw) {
127
+ const trimmed = raw.trim();
128
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
129
+ (trimmed.startsWith('<') && trimmed.endsWith('>'))) {
130
+ return trimmed.slice(1, -1);
131
+ }
132
+ return trimmed;
133
+ }
134
+ function isSystemHeaderSpelling(raw) {
135
+ const trimmed = raw.trim();
136
+ return trimmed.startsWith('<') && trimmed.endsWith('>');
137
+ }
138
+ function cleanType(raw) {
139
+ if (raw === undefined)
140
+ return undefined;
141
+ const text = raw
142
+ .replace(/^\(/, '')
143
+ .replace(/\)$/, '')
144
+ .replace(/\b(?:nullable|nonnull|__nullable|__nonnull|_Nullable|_Nonnull|const)\b/g, ' ')
145
+ .replace(/\s+/g, ' ')
146
+ .trim();
147
+ if (text.length === 0)
148
+ return undefined;
149
+ return text;
150
+ }
151
+ export function parseObjCType(raw) {
152
+ const text = cleanType(raw);
153
+ if (text === undefined)
154
+ return undefined;
155
+ if (text === 'id' || text === 'instancetype')
156
+ return { kind: 'dynamic', raw: text };
157
+ if (text === 'Class')
158
+ return { kind: 'class-object', raw: text };
159
+ const protocolMatch = text.match(/^id\s*<\s*([A-Za-z_][A-Za-z0-9_]*)\s*>/);
160
+ if (protocolMatch !== null) {
161
+ return { kind: 'protocol', name: protocolMatch[1], raw: text };
162
+ }
163
+ const classMatch = text.match(/([A-Za-z_][A-Za-z0-9_]*)\s*(?:<[^>]+>)?\s*\*?$/);
164
+ if (classMatch !== null) {
165
+ return { kind: 'class', name: classMatch[1], raw: text };
166
+ }
167
+ return { kind: 'unknown', raw: text };
168
+ }
169
+ function methodKind(node) {
170
+ const first = node.child(0)?.text;
171
+ return first === '+' ? '+' : '-';
172
+ }
173
+ function methodTypeText(node) {
174
+ const typeNode = directNamedChildren(node).find((child) => child.type === 'method_type');
175
+ return cleanType(typeNode?.text);
176
+ }
177
+ function methodSelector(node) {
178
+ const children = directChildren(node);
179
+ const keywordDeclarators = children.filter((child) => child.type === 'keyword_declarator');
180
+ if (keywordDeclarators.length > 0) {
181
+ return keywordDeclarators
182
+ .map((declarator) => directIdentifiers(declarator)[0]?.text)
183
+ .filter((name) => name !== undefined)
184
+ .map((name) => `${name}:`)
185
+ .join('');
186
+ }
187
+ const pieces = [];
188
+ for (let i = 0; i < children.length; i++) {
189
+ const child = children[i];
190
+ if (child.type !== 'identifier')
191
+ continue;
192
+ const next = children[i + 1];
193
+ if (next?.type === 'method_parameter') {
194
+ pieces.push(`${child.text}:`);
195
+ continue;
196
+ }
197
+ if (pieces.length === 0)
198
+ pieces.push(child.text);
199
+ }
200
+ return pieces.join('');
201
+ }
202
+ function messageSelector(node) {
203
+ const children = directChildren(node);
204
+ const pieces = [];
205
+ for (let i = 2; i < children.length; i++) {
206
+ const child = children[i];
207
+ if (child.type !== 'identifier' && child.type !== 'field_identifier')
208
+ continue;
209
+ const next = children[i + 1];
210
+ if (next?.text === ':')
211
+ pieces.push(`${child.text}:`);
212
+ else if (pieces.length === 0 && next?.text === ']')
213
+ pieces.push(child.text);
214
+ }
215
+ return pieces.join('');
216
+ }
217
+ function methodParameterInfo(node) {
218
+ const parameterTypes = [];
219
+ const parameterNames = [];
220
+ const typeBindings = new Map();
221
+ for (const child of directNamedChildren(node)) {
222
+ if (child.type !== 'method_parameter' && child.type !== 'keyword_declarator')
223
+ continue;
224
+ const named = directNamedChildren(child);
225
+ const typeNode = named.find((n) => n.type === 'method_type');
226
+ const nameNode = [...named].reverse().find((n) => n.type === 'identifier');
227
+ const typeText = cleanType(typeNode?.text);
228
+ if (typeText !== undefined)
229
+ parameterTypes.push(typeText);
230
+ if (nameNode !== undefined) {
231
+ parameterNames.push(nameNode.text);
232
+ const parsed = parseObjCType(typeText);
233
+ if (parsed !== undefined)
234
+ typeBindings.set(nameNode.text, parsed);
235
+ }
236
+ }
237
+ return { parameterTypes, parameterNames, typeBindings };
238
+ }
239
+ function propertyInfos(node) {
240
+ const structDecl = directNamedChildren(node).find((child) => child.type === 'struct_declaration');
241
+ if (structDecl === undefined)
242
+ return [];
243
+ return declarationNamesAndType(structDecl);
244
+ }
245
+ function declarationNameAndType(node) {
246
+ return declarationNamesAndType(node)[0] ?? null;
247
+ }
248
+ function declarationNamesAndType(node) {
249
+ const type = cleanType(firstTypeNode(node)?.text);
250
+ return declaratorNames(node).map((name) => (type !== undefined ? { name, type } : { name }));
251
+ }
252
+ function declaratorNames(node) {
253
+ const names = [];
254
+ collectDeclaratorNames(node, names);
255
+ return names;
256
+ }
257
+ function collectDeclaratorNames(node, out) {
258
+ if (node.type === 'identifier') {
259
+ out.push(node.text);
260
+ return;
261
+ }
262
+ if (node.type === 'init_declarator' ||
263
+ node.type === 'pointer_declarator' ||
264
+ node.type === 'array_declarator' ||
265
+ node.type === 'function_declarator' ||
266
+ node.type === 'parenthesized_declarator' ||
267
+ node.type === 'struct_declarator') {
268
+ for (const child of directNamedChildren(node))
269
+ collectDeclaratorNames(child, out);
270
+ return;
271
+ }
272
+ for (const child of directNamedChildren(node)) {
273
+ if (child.type === 'identifier' ||
274
+ child.type === 'init_declarator' ||
275
+ child.type === 'pointer_declarator' ||
276
+ child.type === 'array_declarator' ||
277
+ child.type === 'function_declarator' ||
278
+ child.type === 'parenthesized_declarator' ||
279
+ child.type === 'struct_declarator' ||
280
+ child.type === 'struct_declaration') {
281
+ collectDeclaratorNames(child, out);
282
+ }
283
+ }
284
+ }
285
+ function declaratorName(node) {
286
+ if (node.type === 'identifier')
287
+ return node.text;
288
+ if (node.type === 'init_declarator') {
289
+ for (const child of directNamedChildren(node)) {
290
+ const name = declaratorName(child);
291
+ if (name !== undefined)
292
+ return name;
293
+ }
294
+ return undefined;
295
+ }
296
+ if (node.type === 'pointer_declarator' ||
297
+ node.type === 'array_declarator' ||
298
+ node.type === 'function_declarator' ||
299
+ node.type === 'parenthesized_declarator' ||
300
+ node.type === 'struct_declarator') {
301
+ for (const child of directNamedChildren(node)) {
302
+ const name = declaratorName(child);
303
+ if (name !== undefined)
304
+ return name;
305
+ }
306
+ return undefined;
307
+ }
308
+ for (const child of directNamedChildren(node)) {
309
+ if (child.type === 'identifier' ||
310
+ child.type === 'init_declarator' ||
311
+ child.type === 'pointer_declarator' ||
312
+ child.type === 'array_declarator' ||
313
+ child.type === 'function_declarator' ||
314
+ child.type === 'parenthesized_declarator' ||
315
+ child.type === 'struct_declarator' ||
316
+ child.type === 'struct_declaration') {
317
+ const name = declaratorName(child);
318
+ if (name !== undefined)
319
+ return name;
320
+ }
321
+ }
322
+ return undefined;
323
+ }
324
+ function firstTypeNode(node) {
325
+ if (node.type === 'type_identifier' ||
326
+ node.type === 'primitive_type' ||
327
+ node.type === 'typedefed_specifier') {
328
+ return node;
329
+ }
330
+ for (const child of directNamedChildren(node)) {
331
+ const found = firstTypeNode(child);
332
+ if (found !== undefined)
333
+ return found;
334
+ }
335
+ return undefined;
336
+ }
337
+ function functionInfos(node) {
338
+ const declarators = [];
339
+ const collect = (current) => {
340
+ for (const child of directNamedChildren(current)) {
341
+ if (child.type === 'function_declarator') {
342
+ declarators.push(child);
343
+ continue;
344
+ }
345
+ collect(child);
346
+ }
347
+ };
348
+ collect(node);
349
+ const returnType = cleanType(directNamedChildren(node).find((child) => child.type === 'type_identifier' ||
350
+ child.type === 'primitive_type' ||
351
+ child.type === 'typedefed_specifier')?.text);
352
+ const linkage = directNamedChildren(node).some((child) => child.type === 'storage_class_specifier' && child.text === 'static')
353
+ ? 'internal'
354
+ : 'external';
355
+ return declarators.flatMap((declarator) => {
356
+ // `int (*callback)(int)` is a function pointer declaration, not a callable
357
+ // definition. A pointer return (`int *func(void)`) remains a C function.
358
+ if (declarator.childForFieldName('declarator')?.type === 'parenthesized_declarator') {
359
+ return [];
360
+ }
361
+ const name = declaratorName(declarator);
362
+ if (name === undefined)
363
+ return [];
364
+ const parameterTypes = [];
365
+ walkNamedTree(declarator, (child) => {
366
+ if (child.type !== 'parameter_declaration')
367
+ return;
368
+ const typeNode = directNamedChildren(child).find((n) => n.type === 'type_identifier' ||
369
+ n.type === 'primitive_type' ||
370
+ n.type === 'typedefed_specifier');
371
+ const typeText = cleanType(typeNode?.text);
372
+ if (typeText !== undefined)
373
+ parameterTypes.push(typeText);
374
+ });
375
+ return [{ name, linkage, returnType, parameterTypes }];
376
+ });
377
+ }
378
+ function directProtocolNames(node) {
379
+ const out = [];
380
+ for (const child of directNamedChildren(node)) {
381
+ if (child.type === 'protocol_reference_list') {
382
+ out.push(...directIdentifiers(child).map((id) => id.text));
383
+ }
384
+ else if (child.type === 'parameterized_arguments') {
385
+ for (const named of directNamedChildren(child)) {
386
+ if (named.type !== 'type_name')
387
+ continue;
388
+ const id = directNamedChildren(named).find((n) => n.type === 'type_identifier');
389
+ if (id !== undefined)
390
+ out.push(id.text);
391
+ }
392
+ }
393
+ }
394
+ return Array.from(new Set(out));
395
+ }
396
+ function parseContainer(node, filePath) {
397
+ if (node.type !== 'class_interface' &&
398
+ node.type !== 'class_implementation' &&
399
+ node.type !== 'protocol_declaration') {
400
+ return null;
401
+ }
402
+ const ids = directIdentifiers(node);
403
+ const first = ids[0];
404
+ if (first === undefined)
405
+ return null;
406
+ const { startLine, endLine } = range(node);
407
+ if (node.type === 'protocol_declaration') {
408
+ const qualifiedName = objcProtocolQualifiedName(first.text);
409
+ return {
410
+ kind: 'protocol',
411
+ declarationRole: 'interface',
412
+ name: first.text,
413
+ qualifiedName,
414
+ nodeId: graphNodeId('Protocol', qualifiedName),
415
+ label: 'Protocol',
416
+ filePath,
417
+ startLine,
418
+ endLine,
419
+ protocols: directProtocolNames(node).filter((name) => name !== first.text),
420
+ };
421
+ }
422
+ const children = directChildren(node);
423
+ const openParenIndex = children.findIndex((child) => child.text === '(');
424
+ if (openParenIndex !== -1) {
425
+ const categoryIdentifier = children
426
+ .slice(openParenIndex + 1)
427
+ .find((child) => child.type === 'identifier');
428
+ const categoryName = categoryIdentifier?.text ?? '__extension__';
429
+ const kind = categoryIdentifier === undefined ? 'extension' : 'category';
430
+ const qualifiedName = objcCategoryQualifiedName(first.text, categoryName);
431
+ return {
432
+ kind,
433
+ declarationRole: node.type === 'class_interface' ? 'interface' : 'implementation',
434
+ name: `${first.text} (${categoryIdentifier?.text ?? ''})`,
435
+ qualifiedName,
436
+ nodeId: graphNodeId('Category', qualifiedName),
437
+ label: 'Category',
438
+ filePath,
439
+ startLine,
440
+ endLine,
441
+ protocols: directProtocolNames(node),
442
+ hostClass: first.text,
443
+ categoryName,
444
+ };
445
+ }
446
+ const colonIndex = children.findIndex((child) => child.text === ':');
447
+ const superclass = colonIndex !== -1
448
+ ? children.slice(colonIndex + 1).find((child) => child.type === 'identifier')?.text
449
+ : undefined;
450
+ const qualifiedName = objcClassQualifiedName(first.text);
451
+ return {
452
+ kind: 'class',
453
+ declarationRole: node.type === 'class_interface' ? 'interface' : 'implementation',
454
+ name: first.text,
455
+ qualifiedName,
456
+ nodeId: graphNodeId('Class', qualifiedName),
457
+ label: 'Class',
458
+ filePath,
459
+ startLine,
460
+ endLine,
461
+ ...(superclass !== undefined ? { superclass } : {}),
462
+ protocols: directProtocolNames(node),
463
+ };
464
+ }
465
+ function enclosingCompoundScope(node, methodBody) {
466
+ let current = node.parent;
467
+ while (current !== null && current !== methodBody) {
468
+ if (current.type === 'compound_statement')
469
+ return current;
470
+ current = current.parent;
471
+ }
472
+ return methodBody;
473
+ }
474
+ function collectMethodBindings(methodNode, parameterTypes) {
475
+ const methodBody = directNamedChildren(methodNode).find((child) => child.type === 'compound_statement');
476
+ if (methodBody === undefined)
477
+ return [];
478
+ const bindings = [...parameterTypes].map(([name, type]) => ({
479
+ name,
480
+ type,
481
+ declarationIndex: methodBody.startIndex,
482
+ scopeStartIndex: methodBody.startIndex,
483
+ scopeEndIndex: methodBody.endIndex,
484
+ }));
485
+ walkNamedTree(methodBody, (node) => {
486
+ if (node.type !== 'declaration')
487
+ return;
488
+ const info = declarationNameAndType(node);
489
+ if (info === null)
490
+ return;
491
+ const type = parseObjCType(info.type);
492
+ if (type === undefined)
493
+ return;
494
+ const scope = enclosingCompoundScope(node, methodBody);
495
+ bindings.push({
496
+ name: info.name,
497
+ type,
498
+ declarationIndex: node.endIndex,
499
+ scopeStartIndex: scope.startIndex,
500
+ scopeEndIndex: scope.endIndex,
501
+ });
502
+ });
503
+ return bindings;
504
+ }
505
+ function bindingAt(bindings, name, sourceIndex) {
506
+ const matches = bindings.filter((binding) => binding.name === name &&
507
+ binding.declarationIndex <= sourceIndex &&
508
+ binding.scopeStartIndex <= sourceIndex &&
509
+ sourceIndex <= binding.scopeEndIndex);
510
+ matches.sort((left, right) => {
511
+ const leftScope = left.scopeEndIndex - left.scopeStartIndex;
512
+ const rightScope = right.scopeEndIndex - right.scopeStartIndex;
513
+ return leftScope - rightScope || right.declarationIndex - left.declarationIndex;
514
+ });
515
+ return matches[0]?.type;
516
+ }
517
+ function isReflectionSelector(selector, receiverText) {
518
+ return (selector.startsWith('performSelector:') ||
519
+ selector === 'methodForSelector:' ||
520
+ selector === 'forwardInvocation:' ||
521
+ receiverText === 'NSInvocation');
522
+ }
523
+ function isPascalCaseLike(text) {
524
+ if (!/^[A-Z][A-Za-z0-9]*$/.test(text))
525
+ return false;
526
+ return text.length === 1 || /[a-z0-9]/.test(text.slice(1));
527
+ }
528
+ function messageReceiver(node) {
529
+ return directNamedChildren(node)[0] ?? null;
530
+ }
531
+ function macroName(node) {
532
+ return node.childForFieldName('name')?.text ?? directIdentifiers(node)[0]?.text;
533
+ }
534
+ function classifyReceiver(receiver, selector, method, methodBindings, membersByOwner, macroNames, classNames) {
535
+ const text = receiver.text;
536
+ if (isReflectionSelector(selector, text)) {
537
+ return {
538
+ kind: 'dynamic',
539
+ unresolvedReason: 'reflection selector dispatch is dynamic',
540
+ };
541
+ }
542
+ const receiverMacroName = receiver.type === 'call_expression'
543
+ ? receiver.childForFieldName('function')?.text
544
+ : receiver.type === 'identifier'
545
+ ? text
546
+ : undefined;
547
+ if (receiverMacroName !== undefined && macroNames.has(receiverMacroName)) {
548
+ return {
549
+ kind: 'dynamic',
550
+ type: { kind: 'dynamic', raw: text },
551
+ unresolvedReason: `macro receiver ${receiverMacroName} is dynamic`,
552
+ };
553
+ }
554
+ if (receiver.type === 'identifier') {
555
+ if (text === 'self')
556
+ return { kind: 'self' };
557
+ if (text === 'super')
558
+ return { kind: 'super' };
559
+ const bound = bindingAt(methodBindings, text, receiver.startIndex);
560
+ if (bound !== undefined) {
561
+ if (bound.kind === 'dynamic' || bound.kind === 'class-object') {
562
+ return {
563
+ kind: 'dynamic',
564
+ type: bound,
565
+ unresolvedReason: `${bound.raw} receiver is dynamic`,
566
+ };
567
+ }
568
+ return { kind: 'local', type: bound };
569
+ }
570
+ const hostOwner = method.hostClass !== undefined
571
+ ? objcClassQualifiedName(method.hostClass)
572
+ : method.ownerQualifiedName;
573
+ const memberType = membersByOwner.get(hostOwner)?.get(text);
574
+ if (memberType !== undefined)
575
+ return { kind: 'ivar', type: memberType };
576
+ if (classNames.has(text) || isPascalCaseLike(text)) {
577
+ return { kind: 'class', type: { kind: 'class', name: text, raw: text } };
578
+ }
579
+ return { kind: 'unknown', memberName: text };
580
+ }
581
+ if (receiver.type === 'field_expression') {
582
+ const parts = directNamedChildren(receiver);
583
+ const base = parts[0];
584
+ const field = parts[1];
585
+ if (base?.text === 'self' && field !== undefined) {
586
+ const hostOwner = method.hostClass !== undefined
587
+ ? objcClassQualifiedName(method.hostClass)
588
+ : method.ownerQualifiedName;
589
+ const memberType = membersByOwner.get(hostOwner)?.get(field.text);
590
+ if (memberType !== undefined)
591
+ return { kind: 'property', type: memberType };
592
+ return { kind: 'property', memberName: field.text };
593
+ }
594
+ }
595
+ return {
596
+ kind: 'unknown',
597
+ unresolvedReason: 'receiver expression is not statically typed',
598
+ };
599
+ }
600
+ function collectMethodMessages(methodNode, method, parameterTypes, membersByOwner, macroNames, classNames, messages, unresolvedMessages) {
601
+ const methodBindings = collectMethodBindings(methodNode, parameterTypes);
602
+ walkNamedTree(methodNode, (messageNode) => {
603
+ if (messageNode.type !== 'message_expression')
604
+ return;
605
+ const receiver = messageReceiver(messageNode);
606
+ if (receiver === null)
607
+ return;
608
+ const selector = messageSelector(messageNode);
609
+ if (selector.length === 0)
610
+ return;
611
+ const classified = classifyReceiver(receiver, selector, method, methodBindings, membersByOwner, macroNames, classNames);
612
+ const fact = {
613
+ selector,
614
+ receiverText: receiver.text,
615
+ receiverKind: classified.kind,
616
+ ...(classified.type !== undefined ? { receiverType: classified.type } : {}),
617
+ ...(classified.memberName !== undefined ? { receiverMemberName: classified.memberName } : {}),
618
+ sourceMethodQualifiedName: method.qualifiedName,
619
+ sourceMethodId: method.nodeId,
620
+ sourceOwnerQualifiedName: method.ownerQualifiedName,
621
+ sourceOwnerName: method.ownerName,
622
+ sourceMethodKind: method.methodKind,
623
+ filePath: method.filePath,
624
+ startLine: messageNode.startPosition.row,
625
+ startCol: messageNode.startPosition.column,
626
+ };
627
+ if (classified.unresolvedReason !== undefined) {
628
+ unresolvedMessages.push({
629
+ selector,
630
+ receiverText: receiver.text,
631
+ reason: classified.unresolvedReason,
632
+ sourceMethodQualifiedName: method.qualifiedName,
633
+ sourceMethodId: method.nodeId,
634
+ filePath: method.filePath,
635
+ startLine: messageNode.startPosition.row,
636
+ startCol: messageNode.startPosition.column,
637
+ });
638
+ }
639
+ messages.push(fact);
640
+ });
641
+ }
642
+ export function collectObjectiveCFacts(tree, filePath) {
643
+ const containers = [];
644
+ const methods = [];
645
+ const members = [];
646
+ const functions = [];
647
+ const imports = [];
648
+ const messages = [];
649
+ const unresolvedMessages = [];
650
+ const membersByOwner = new Map();
651
+ const macroNames = new Set();
652
+ const classNames = new Set();
653
+ const addFunctionFact = (node) => {
654
+ for (const info of functionInfos(node)) {
655
+ const qualifiedName = objcFunctionQualifiedName(info.name, info.linkage, filePath);
656
+ const { startLine, endLine } = range(node);
657
+ functions.push({
658
+ name: info.name,
659
+ linkage: info.linkage,
660
+ qualifiedName,
661
+ nodeId: graphNodeId('Function', qualifiedName),
662
+ filePath,
663
+ startLine,
664
+ endLine,
665
+ ...(info.returnType !== undefined ? { returnType: info.returnType } : {}),
666
+ parameterTypes: info.parameterTypes,
667
+ });
668
+ }
669
+ };
670
+ walkNamedTree(tree.rootNode, (node) => {
671
+ if (node.type !== 'preproc_def' && node.type !== 'preproc_function_def')
672
+ return;
673
+ const name = macroName(node);
674
+ if (name !== undefined)
675
+ macroNames.add(name);
676
+ });
677
+ walkNamedTree(tree.rootNode, (node) => {
678
+ const container = parseContainer(node, filePath);
679
+ if (container?.kind === 'class')
680
+ classNames.add(container.name);
681
+ });
682
+ const addMemberType = (ownerQualifiedName, name, typeText) => {
683
+ const typeInfo = parseObjCType(typeText);
684
+ if (typeInfo === undefined)
685
+ return;
686
+ let byName = membersByOwner.get(ownerQualifiedName);
687
+ if (byName === undefined) {
688
+ byName = new Map();
689
+ membersByOwner.set(ownerQualifiedName, byName);
690
+ }
691
+ byName.set(name, typeInfo);
692
+ };
693
+ const collectContainerMemberTypes = (containerNode, container) => {
694
+ for (const inner of containerMemberNodes(containerNode)) {
695
+ if (inner.type === 'property_declaration') {
696
+ for (const prop of propertyInfos(inner)) {
697
+ addMemberType(container.qualifiedName, prop.name, prop.type);
698
+ if (container.hostClass !== undefined) {
699
+ addMemberType(objcClassQualifiedName(container.hostClass), prop.name, prop.type);
700
+ }
701
+ }
702
+ }
703
+ else if (inner.type === 'instance_variables') {
704
+ walkNamedTree(inner, (ivarNode) => {
705
+ if (ivarNode.type !== 'instance_variable')
706
+ return;
707
+ for (const ivar of declarationNamesAndType(ivarNode)) {
708
+ addMemberType(container.qualifiedName, ivar.name, ivar.type);
709
+ if (container.hostClass !== undefined) {
710
+ addMemberType(objcClassQualifiedName(container.hostClass), ivar.name, ivar.type);
711
+ }
712
+ }
713
+ });
714
+ }
715
+ }
716
+ };
717
+ const topLevelNodes = logicalTopLevelNodes(tree.rootNode);
718
+ for (const child of topLevelNodes) {
719
+ const container = parseContainer(child, filePath);
720
+ if (container !== null)
721
+ collectContainerMemberTypes(child, container);
722
+ }
723
+ for (const child of topLevelNodes) {
724
+ if (child.type === 'preproc_include') {
725
+ const rawNode = directNamedChildren(child).find((n) => n.type === 'string_literal' || n.type === 'system_lib_string');
726
+ if (rawNode !== undefined) {
727
+ const raw = rawNode.text;
728
+ const directive = child.text.trimStart().startsWith('#include') ? 'include' : 'import';
729
+ const { startLine, endLine } = range(child);
730
+ imports.push({
731
+ kind: directive,
732
+ raw,
733
+ targetRaw: stripQuotes(raw),
734
+ filePath,
735
+ startLine,
736
+ endLine,
737
+ });
738
+ }
739
+ continue;
740
+ }
741
+ if (child.type === 'module_import') {
742
+ const moduleName = directIdentifiers(child)
743
+ .map((n) => n.text)
744
+ .join('.');
745
+ const { startLine, endLine } = range(child);
746
+ imports.push({
747
+ kind: 'module',
748
+ raw: moduleName,
749
+ targetRaw: moduleName,
750
+ filePath,
751
+ startLine,
752
+ endLine,
753
+ });
754
+ continue;
755
+ }
756
+ const container = parseContainer(child, filePath);
757
+ if (container !== null) {
758
+ containers.push(container);
759
+ for (const inner of containerMemberNodes(child)) {
760
+ if (inner.type === 'method_declaration' || inner.type === 'method_definition') {
761
+ const selector = methodSelector(inner);
762
+ const { parameterTypes, parameterNames, typeBindings } = methodParameterInfo(inner);
763
+ const kind = methodKind(inner);
764
+ const qualifiedName = objcMethodQualifiedName(container.qualifiedName, kind, selector);
765
+ const { startLine, endLine } = range(inner);
766
+ const method = {
767
+ name: selector,
768
+ selector,
769
+ methodKind: kind,
770
+ ownerQualifiedName: container.qualifiedName,
771
+ ownerName: container.name,
772
+ ownerKind: container.kind,
773
+ ...(container.hostClass !== undefined ? { hostClass: container.hostClass } : {}),
774
+ qualifiedName,
775
+ nodeId: graphNodeId('Method', qualifiedName),
776
+ filePath,
777
+ startLine,
778
+ endLine,
779
+ declarationRole: inner.type === 'method_definition' ? 'implementation' : 'declaration',
780
+ ...(methodTypeText(inner) !== undefined ? { returnType: methodTypeText(inner) } : {}),
781
+ parameterTypes,
782
+ parameterNames,
783
+ };
784
+ methods.push(method);
785
+ collectMethodMessages(inner, method, typeBindings, membersByOwner, macroNames, classNames, messages, unresolvedMessages);
786
+ }
787
+ else if (inner.type === 'property_declaration') {
788
+ const { startLine, endLine } = range(inner);
789
+ for (const prop of propertyInfos(inner)) {
790
+ const qualifiedName = objcPropertyQualifiedName(container.qualifiedName, prop.name);
791
+ members.push({
792
+ kind: 'property',
793
+ name: prop.name,
794
+ qualifiedName,
795
+ nodeId: graphNodeId('Property', qualifiedName),
796
+ ownerQualifiedName: container.qualifiedName,
797
+ ownerName: container.name,
798
+ ownerKind: container.kind,
799
+ ownerLabel: ownerLabel(container.kind),
800
+ ...(container.hostClass !== undefined ? { hostClass: container.hostClass } : {}),
801
+ ...(prop.type !== undefined ? { declaredType: prop.type } : {}),
802
+ filePath,
803
+ startLine,
804
+ endLine,
805
+ });
806
+ }
807
+ }
808
+ else if (inner.type === 'instance_variables') {
809
+ walkNamedTree(inner, (ivarNode) => {
810
+ if (ivarNode.type !== 'instance_variable')
811
+ return;
812
+ const { startLine, endLine } = range(ivarNode);
813
+ for (const ivar of declarationNamesAndType(ivarNode)) {
814
+ const qualifiedName = objcIvarQualifiedName(container.qualifiedName, ivar.name);
815
+ members.push({
816
+ kind: 'ivar',
817
+ name: ivar.name,
818
+ qualifiedName,
819
+ nodeId: graphNodeId('Variable', qualifiedName),
820
+ ownerQualifiedName: container.qualifiedName,
821
+ ownerName: container.name,
822
+ ownerKind: container.kind,
823
+ ownerLabel: ownerLabel(container.kind),
824
+ ...(container.hostClass !== undefined ? { hostClass: container.hostClass } : {}),
825
+ ...(ivar.type !== undefined ? { declaredType: ivar.type } : {}),
826
+ filePath,
827
+ startLine,
828
+ endLine,
829
+ });
830
+ }
831
+ });
832
+ }
833
+ else if (inner.type === 'implementation_definition') {
834
+ const functionNode = directNamedChildren(inner).find((node) => node.type === 'function_definition');
835
+ if (functionNode !== undefined) {
836
+ addFunctionFact(functionNode);
837
+ continue;
838
+ }
839
+ const methodNode = directNamedChildren(inner).find((n) => n.type === 'method_definition');
840
+ if (methodNode !== undefined) {
841
+ const selector = methodSelector(methodNode);
842
+ const { parameterTypes, parameterNames, typeBindings } = methodParameterInfo(methodNode);
843
+ const kind = methodKind(methodNode);
844
+ const qualifiedName = objcMethodQualifiedName(container.qualifiedName, kind, selector);
845
+ const { startLine, endLine } = range(methodNode);
846
+ const method = {
847
+ name: selector,
848
+ selector,
849
+ methodKind: kind,
850
+ ownerQualifiedName: container.qualifiedName,
851
+ ownerName: container.name,
852
+ ownerKind: container.kind,
853
+ ...(container.hostClass !== undefined ? { hostClass: container.hostClass } : {}),
854
+ qualifiedName,
855
+ nodeId: graphNodeId('Method', qualifiedName),
856
+ filePath,
857
+ startLine,
858
+ endLine,
859
+ declarationRole: 'implementation',
860
+ ...(methodTypeText(methodNode) !== undefined
861
+ ? { returnType: methodTypeText(methodNode) }
862
+ : {}),
863
+ parameterTypes,
864
+ parameterNames,
865
+ };
866
+ methods.push(method);
867
+ collectMethodMessages(methodNode, method, typeBindings, membersByOwner, macroNames, classNames, messages, unresolvedMessages);
868
+ }
869
+ }
870
+ }
871
+ continue;
872
+ }
873
+ if (child.type === 'function_definition' || child.type === 'declaration') {
874
+ addFunctionFact(child);
875
+ }
876
+ }
877
+ return {
878
+ providerVersion: OBJECTIVE_C_PROVIDER_VERSION,
879
+ grammarPackage: OBJECTIVE_C_GRAMMAR_PACKAGE,
880
+ grammarVersion: OBJECTIVE_C_GRAMMAR_VERSION,
881
+ filePath,
882
+ containers,
883
+ methods,
884
+ members,
885
+ functions,
886
+ imports,
887
+ messages,
888
+ unresolvedMessages,
889
+ };
890
+ }
891
+ function semanticNode(label, id, name, qualifiedName, filePath, startLine, endLine, extras = {}) {
892
+ return {
893
+ id,
894
+ label,
895
+ properties: {
896
+ name,
897
+ filePath,
898
+ startLine,
899
+ endLine,
900
+ language: SupportedLanguages.ObjectiveC,
901
+ isExported: true,
902
+ qualifiedName,
903
+ objectiveCProviderVersion: OBJECTIVE_C_PROVIDER_VERSION,
904
+ objectiveCGrammar: `${OBJECTIVE_C_GRAMMAR_PACKAGE}@${OBJECTIVE_C_GRAMMAR_VERSION}`,
905
+ ...extras,
906
+ },
907
+ };
908
+ }
909
+ function relationship(type, sourceId, targetId, reason) {
910
+ return {
911
+ id: generateId(type, `${sourceId}->${targetId}:${reason}`),
912
+ sourceId,
913
+ targetId,
914
+ type,
915
+ confidence: 1,
916
+ reason,
917
+ };
918
+ }
919
+ function implementationEvidenceNode(qualifiedName, name, filePath, startLine, endLine, extras) {
920
+ return semanticNode('CodeElement', graphNodeId('CodeElement', qualifiedName), name, qualifiedName, filePath, startLine, endLine, {
921
+ objectiveCKind: 'implementation-evidence',
922
+ ...extras,
923
+ });
924
+ }
925
+ export function buildObjectiveCSemanticGraph(facts) {
926
+ const nodes = [];
927
+ const relationships = [];
928
+ const symbols = [];
929
+ const fileId = generateId('File', facts.filePath);
930
+ for (const container of facts.containers) {
931
+ nodes.push(semanticNode(container.label, container.nodeId, container.name, container.qualifiedName, facts.filePath, container.startLine, container.endLine, {
932
+ objectiveCKind: container.kind,
933
+ declarationRole: container.declarationRole,
934
+ ...(container.superclass !== undefined ? { superclass: container.superclass } : {}),
935
+ ...(container.protocols.length > 0 ? { protocols: [...container.protocols] } : {}),
936
+ ...(container.hostClass !== undefined ? { hostClass: container.hostClass } : {}),
937
+ ...(container.categoryName !== undefined ? { categoryName: container.categoryName } : {}),
938
+ }));
939
+ relationships.push(relationship('DEFINES', fileId, container.nodeId, 'objc-definition'));
940
+ if (container.declarationRole === 'implementation') {
941
+ const qualifiedName = `objc:implementation:${container.qualifiedName}:${facts.filePath}:${container.startLine}`;
942
+ const evidenceId = graphNodeId('CodeElement', qualifiedName);
943
+ nodes.push(implementationEvidenceNode(qualifiedName, `@implementation ${container.name}`, facts.filePath, container.startLine, container.endLine, {
944
+ implementationKind: container.kind,
945
+ targetQualifiedName: container.qualifiedName,
946
+ }));
947
+ relationships.push(relationship('DEFINES', fileId, evidenceId, 'objc: implementation evidence'));
948
+ relationships.push(relationship('DECLARES', evidenceId, container.nodeId, 'objc: implementation of merged symbol'));
949
+ }
950
+ symbols.push({
951
+ filePath: facts.filePath,
952
+ name: container.name,
953
+ nodeId: container.nodeId,
954
+ type: container.label,
955
+ qualifiedName: container.qualifiedName,
956
+ });
957
+ }
958
+ for (const method of facts.methods) {
959
+ nodes.push(semanticNode('Method', method.nodeId, method.selector, method.qualifiedName, facts.filePath, method.startLine, method.endLine, {
960
+ selector: method.selector,
961
+ methodKind: method.methodKind,
962
+ objectiveCOwner: method.ownerQualifiedName,
963
+ objectiveCOwnerName: method.ownerName,
964
+ description: `${method.ownerName} ${method.methodKind}${method.selector}`,
965
+ declarationRole: method.declarationRole,
966
+ parameterCount: method.parameterTypes.length,
967
+ parameterTypes: [...method.parameterTypes],
968
+ ...(method.returnType !== undefined ? { returnType: method.returnType } : {}),
969
+ ...(method.hostClass !== undefined ? { hostClass: method.hostClass } : {}),
970
+ }));
971
+ const ownerId = graphNodeId(method.ownerKind === 'class'
972
+ ? 'Class'
973
+ : method.ownerKind === 'protocol'
974
+ ? 'Protocol'
975
+ : 'Category', method.ownerQualifiedName);
976
+ relationships.push(relationship('HAS_METHOD', ownerId, method.nodeId, 'objc-owner-method'));
977
+ if (method.declarationRole === 'implementation') {
978
+ const qualifiedName = `objc:method-implementation:${method.qualifiedName}:${facts.filePath}:${method.startLine}`;
979
+ const evidenceId = graphNodeId('CodeElement', qualifiedName);
980
+ nodes.push(implementationEvidenceNode(qualifiedName, `${method.methodKind}[${method.ownerName} ${method.selector}] implementation`, facts.filePath, method.startLine, method.endLine, {
981
+ implementationKind: 'method',
982
+ targetQualifiedName: method.qualifiedName,
983
+ selector: method.selector,
984
+ methodKind: method.methodKind,
985
+ objectiveCOwner: method.ownerQualifiedName,
986
+ }));
987
+ relationships.push(relationship('DEFINES', fileId, evidenceId, 'objc: implementation evidence'));
988
+ relationships.push(relationship('DECLARES', evidenceId, method.nodeId, 'objc: implementation of merged symbol'));
989
+ }
990
+ if (method.hostClass !== undefined) {
991
+ relationships.push(relationship('HAS_METHOD', graphNodeId('Class', objcClassQualifiedName(method.hostClass)), method.nodeId, 'objc-category-host-method'));
992
+ }
993
+ symbols.push({
994
+ filePath: facts.filePath,
995
+ name: method.selector,
996
+ nodeId: method.nodeId,
997
+ type: 'Method',
998
+ qualifiedName: method.qualifiedName,
999
+ parameterCount: method.parameterTypes.length,
1000
+ requiredParameterCount: method.parameterTypes.length,
1001
+ parameterTypes: [...method.parameterTypes],
1002
+ ...(method.returnType !== undefined ? { returnType: method.returnType } : {}),
1003
+ ownerId,
1004
+ isStatic: method.methodKind === '+',
1005
+ });
1006
+ }
1007
+ for (const member of facts.members) {
1008
+ const label = member.kind === 'property' ? 'Property' : 'Variable';
1009
+ nodes.push(semanticNode(label, member.nodeId, member.name, member.qualifiedName, facts.filePath, member.startLine, member.endLine, {
1010
+ objectiveCKind: member.kind,
1011
+ ...(member.declaredType !== undefined ? { declaredType: member.declaredType } : {}),
1012
+ }));
1013
+ relationships.push(relationship(member.kind === 'property' ? 'HAS_PROPERTY' : 'HAS_PROPERTY', graphNodeId(member.ownerLabel, member.ownerQualifiedName), member.nodeId, `objc-${member.kind}`));
1014
+ if (member.hostClass !== undefined) {
1015
+ relationships.push(relationship('HAS_PROPERTY', graphNodeId('Class', objcClassQualifiedName(member.hostClass)), member.nodeId, `objc-category-host-${member.kind}`));
1016
+ }
1017
+ symbols.push({
1018
+ filePath: facts.filePath,
1019
+ name: member.name,
1020
+ nodeId: member.nodeId,
1021
+ type: label,
1022
+ qualifiedName: member.qualifiedName,
1023
+ ...(member.declaredType !== undefined ? { declaredType: member.declaredType } : {}),
1024
+ ownerId: graphNodeId(member.ownerLabel, member.ownerQualifiedName),
1025
+ });
1026
+ }
1027
+ for (const fn of facts.functions) {
1028
+ nodes.push(semanticNode('Function', fn.nodeId, fn.name, fn.qualifiedName, facts.filePath, fn.startLine, fn.endLine, {
1029
+ parameterCount: fn.parameterTypes.length,
1030
+ parameterTypes: [...fn.parameterTypes],
1031
+ linkage: fn.linkage,
1032
+ ...(fn.returnType !== undefined ? { returnType: fn.returnType } : {}),
1033
+ }));
1034
+ relationships.push(relationship('DEFINES', fileId, fn.nodeId, 'objc-c-function'));
1035
+ symbols.push({
1036
+ filePath: facts.filePath,
1037
+ name: fn.name,
1038
+ nodeId: fn.nodeId,
1039
+ type: 'Function',
1040
+ qualifiedName: fn.qualifiedName,
1041
+ parameterCount: fn.parameterTypes.length,
1042
+ parameterTypes: [...fn.parameterTypes],
1043
+ ...(fn.returnType !== undefined ? { returnType: fn.returnType } : {}),
1044
+ });
1045
+ }
1046
+ for (const imp of facts.imports) {
1047
+ const qualifiedName = `objc:import:${facts.filePath}:${imp.startLine}:${imp.kind}:${imp.targetRaw}`;
1048
+ const id = graphNodeId('Import', qualifiedName);
1049
+ nodes.push(semanticNode('Import', id, imp.targetRaw, qualifiedName, facts.filePath, imp.startLine, imp.endLine, {
1050
+ objectiveCKind: 'import',
1051
+ importKind: imp.kind,
1052
+ raw: imp.raw,
1053
+ targetRaw: imp.targetRaw,
1054
+ }));
1055
+ relationships.push(relationship('DEFINES', fileId, id, 'objc-import'));
1056
+ symbols.push({
1057
+ filePath: facts.filePath,
1058
+ name: imp.targetRaw,
1059
+ nodeId: id,
1060
+ type: 'Import',
1061
+ qualifiedName,
1062
+ });
1063
+ }
1064
+ for (const unresolved of facts.unresolvedMessages) {
1065
+ const qn = objcUnresolvedMessageQualifiedName(facts.filePath, unresolved.startLine, unresolved.startCol, unresolved.selector);
1066
+ const id = graphNodeId('CodeElement', qn);
1067
+ nodes.push(semanticNode('CodeElement', id, `[${unresolved.receiverText} ${unresolved.selector}] unresolved: ${unresolved.reason}`, qn, facts.filePath, unresolved.startLine, unresolved.startLine, {
1068
+ objectiveCKind: 'unresolved-message',
1069
+ selector: unresolved.selector,
1070
+ receiver: unresolved.receiverText,
1071
+ resolution: 'unresolved',
1072
+ }));
1073
+ relationships.push(relationship('DEFINES', fileId, id, `objc-unresolved-message: ${unresolved.reason}`));
1074
+ symbols.push({
1075
+ filePath: facts.filePath,
1076
+ name: `[${unresolved.receiverText} ${unresolved.selector}]`,
1077
+ nodeId: id,
1078
+ type: 'CodeElement',
1079
+ qualifiedName: qn,
1080
+ });
1081
+ }
1082
+ return { nodes, relationships, symbols };
1083
+ }
1084
+ function captureAt(name, text, startLine, endLine) {
1085
+ return {
1086
+ name,
1087
+ text,
1088
+ range: {
1089
+ startLine: startLine + 1,
1090
+ startCol: 0,
1091
+ endLine: endLine + 1,
1092
+ endCol: 0,
1093
+ },
1094
+ };
1095
+ }
1096
+ export function buildObjectiveCScopeCaptures(facts, root) {
1097
+ const captures = [
1098
+ {
1099
+ '@scope.module': nodeToCapture('@scope.module', root),
1100
+ },
1101
+ ];
1102
+ const seenDeclarations = new Set();
1103
+ for (const container of facts.containers) {
1104
+ const kind = declarationCaptureKind(container.kind);
1105
+ if (kind === undefined)
1106
+ continue;
1107
+ if (!seenDeclarations.add(`${kind}:${container.qualifiedName}`))
1108
+ continue;
1109
+ captures.push(declarationMatch(kind, container.kind === 'category' || container.kind === 'extension'
1110
+ ? (container.categoryName ?? container.name)
1111
+ : container.name, container.qualifiedName, container.startLine, container.endLine));
1112
+ }
1113
+ for (const fn of facts.functions) {
1114
+ if (!seenDeclarations.add(`function:${fn.qualifiedName}`))
1115
+ continue;
1116
+ captures.push(declarationMatch('function', fn.name, fn.qualifiedName, fn.startLine, fn.endLine));
1117
+ }
1118
+ for (const imp of facts.imports) {
1119
+ const anchor = captureAt('@import.statement', imp.raw, imp.startLine, imp.endLine);
1120
+ const sourceText = isSystemHeaderSpelling(imp.raw) ? imp.raw : imp.targetRaw;
1121
+ captures.push({
1122
+ '@import.statement': anchor,
1123
+ '@import.source': {
1124
+ ...anchor,
1125
+ name: '@import.source',
1126
+ text: sourceText,
1127
+ },
1128
+ '@import.name': { ...anchor, name: '@import.name', text: sourceText },
1129
+ '@import.kind': { ...anchor, name: '@import.kind', text: imp.kind },
1130
+ });
1131
+ }
1132
+ return captures;
1133
+ }
1134
+ function declarationCaptureKind(kind) {
1135
+ if (kind === 'extension')
1136
+ return undefined;
1137
+ return kind;
1138
+ }
1139
+ function declarationMatch(kind, name, qualifiedName, startLine, endLine) {
1140
+ const anchor = captureAt(`@declaration.${kind}`, name, startLine, endLine);
1141
+ return {
1142
+ [`@declaration.${kind}`]: anchor,
1143
+ '@declaration.name': { ...anchor, name: '@declaration.name', text: name },
1144
+ '@declaration.qualified_name': {
1145
+ ...anchor,
1146
+ name: '@declaration.qualified_name',
1147
+ text: qualifiedName,
1148
+ },
1149
+ };
1150
+ }