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.
- package/dist/_shared/graph/types.d.ts +1 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +2 -0
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/languages.d.ts +1 -0
- package/dist/_shared/languages.d.ts.map +1 -1
- package/dist/_shared/languages.js +1 -0
- package/dist/_shared/languages.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +2 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.d.ts +2 -1
- package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/language-classification.js +3 -1
- package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/class-registry.d.ts +3 -3
- package/dist/_shared/scope-resolution/registries/class-registry.js +3 -3
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.js +2 -0
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/resolve-type-ref.js +2 -0
- package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
- package/dist/cli/analyze-config.d.ts +2 -2
- package/dist/cli/analyze-config.js +10 -33
- package/dist/cli/doctor.js +6 -4
- package/dist/cli/eval-server.js +13 -1
- package/dist/cli/i18n/en.d.ts +4 -2
- package/dist/cli/i18n/en.js +4 -2
- package/dist/cli/i18n/resources.d.ts +6 -2
- package/dist/cli/i18n/zh-CN.d.ts +2 -0
- package/dist/cli/i18n/zh-CN.js +4 -2
- package/dist/core/analysis-feature-registry.d.ts +1 -1
- package/dist/core/analysis-feature-registry.js +2 -0
- package/dist/core/auto-sync/starter.js +2 -2
- package/dist/core/embeddings/ast-utils.js +8 -3
- package/dist/core/embeddings/chunker.js +67 -10
- package/dist/core/embeddings/embedding-pipeline.d.ts +1 -1
- package/dist/core/embeddings/embedding-pipeline.js +1 -1
- package/dist/core/embeddings/structural-extractor.js +2 -2
- package/dist/core/embeddings/types.d.ts +4 -2
- package/dist/core/embeddings/types.js +18 -0
- package/dist/core/git-ref.d.ts +22 -0
- package/dist/core/git-ref.js +114 -0
- package/dist/core/git-staleness.js +14 -0
- package/dist/core/group/extractors/graphql-extractor.d.ts +1 -1
- package/dist/core/group/extractors/graphql-extractor.js +149 -13
- package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
- package/dist/core/group/extractors/manifest-extractor.js +2 -2
- package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +5 -8
- package/dist/core/ingestion/content-language-classification.d.ts +10 -0
- package/dist/core/ingestion/content-language-classification.js +30 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +1 -0
- package/dist/core/ingestion/filesystem-walker.js +1 -1
- package/dist/core/ingestion/import-resolvers/node-workspace-packages.js +3 -2
- package/dist/core/ingestion/language-config.d.ts +82 -13
- package/dist/core/ingestion/language-config.js +217 -13
- package/dist/core/ingestion/language-provider.d.ts +88 -1
- package/dist/core/ingestion/languages/index.d.ts +7 -0
- package/dist/core/ingestion/languages/index.js +30 -1
- package/dist/core/ingestion/languages/objective-c/analysis-features.d.ts +10 -0
- package/dist/core/ingestion/languages/objective-c/analysis-features.js +19 -0
- package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.d.ts +13 -0
- package/dist/core/ingestion/languages/objective-c/compilation-unit-siblings.js +124 -0
- package/dist/core/ingestion/languages/objective-c/facts.d.ts +148 -0
- package/dist/core/ingestion/languages/objective-c/facts.js +1150 -0
- package/dist/core/ingestion/languages/objective-c/import-target.d.ts +11 -0
- package/dist/core/ingestion/languages/objective-c/import-target.js +122 -0
- package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.d.ts +24 -0
- package/dist/core/ingestion/languages/objective-c/macro-marker-preprocess.js +261 -0
- package/dist/core/ingestion/languages/objective-c/resolution-config.d.ts +41 -0
- package/dist/core/ingestion/languages/objective-c/resolution-config.js +282 -0
- package/dist/core/ingestion/languages/objective-c/scope-resolver.d.ts +2 -0
- package/dist/core/ingestion/languages/objective-c/scope-resolver.js +503 -0
- package/dist/core/ingestion/languages/objective-c.d.ts +2 -0
- package/dist/core/ingestion/languages/objective-c.js +300 -0
- package/dist/core/ingestion/languages/typescript/query.d.ts +31 -0
- package/dist/core/ingestion/languages/typescript/query.js +7 -1
- package/dist/core/ingestion/languages/typescript/tsconfig.js +3 -2
- package/dist/core/ingestion/languages/typescript.js +1 -1
- package/dist/core/ingestion/languages/zig/query.d.ts +26 -0
- package/dist/core/ingestion/languages/zig/query.js +61 -1
- package/dist/core/ingestion/languages/zig/range-binding.d.ts +8 -0
- package/dist/core/ingestion/languages/zig/range-binding.js +24 -0
- package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +3 -2
- package/dist/core/ingestion/languages/zig/scope-resolver.js +18 -5
- package/dist/core/ingestion/languages/zig/this-alias-bindings.d.ts +58 -0
- package/dist/core/ingestion/languages/zig/this-alias-bindings.js +176 -0
- package/dist/core/ingestion/languages/zig/workspace-static-gating.d.ts +8 -0
- package/dist/core/ingestion/languages/zig/workspace-static-gating.js +82 -0
- package/dist/core/ingestion/model/registration-table.d.ts +3 -3
- package/dist/core/ingestion/model/registration-table.js +9 -5
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/model/symbol-table.js +2 -0
- package/dist/core/ingestion/model/type-registry.d.ts +1 -1
- package/dist/core/ingestion/parsing-processor.d.ts +22 -6
- package/dist/core/ingestion/parsing-processor.js +39 -20
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +9 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +307 -96
- package/dist/core/ingestion/pipeline-phases/parse-round-budget.d.ts +42 -0
- package/dist/core/ingestion/pipeline-phases/parse-round-budget.js +50 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +7 -1
- package/dist/core/ingestion/scope-extractor.js +4 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +13 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +2 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +2 -0
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.d.ts +78 -5
- package/dist/core/ingestion/scope-resolution/passes/property-dispatch.js +262 -7
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +5 -2
- package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +10 -1
- package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +39 -3
- package/dist/core/ingestion/scope-resolution/scope/walkers.js +82 -3
- package/dist/core/ingestion/scope-resolution/value-ref-edges.d.ts +14 -0
- package/dist/core/ingestion/scope-resolution/value-ref-edges.js +14 -0
- package/dist/core/ingestion/tree-sitter-queries.js +2 -0
- package/dist/core/ingestion/utils/symbol-labels.js +2 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +1 -1
- package/dist/core/ingestion/workers/parse-worker.js +63 -3
- package/dist/core/ingestion/workers/worker-pool.d.ts +56 -2
- package/dist/core/ingestion/workers/worker-pool.js +117 -26
- package/dist/core/lbug/csv-generator.js +2 -0
- package/dist/core/lbug/schema.d.ts +3 -1
- package/dist/core/lbug/schema.js +27 -0
- package/dist/core/search/fts-indexes.js +13 -4
- package/dist/core/search/fts-schema.js +2 -0
- package/dist/core/tree-sitter/parser-loader.js +10 -0
- package/dist/core/tree-sitter/vendored-grammars.d.ts +1 -1
- package/dist/core/tree-sitter/vendored-grammars.js +2 -1
- package/dist/mcp/local/local-backend.d.ts +33 -0
- package/dist/mcp/local/local-backend.js +166 -5
- package/dist/mcp/tools.js +6 -4
- package/dist/server/analyze-job.d.ts +19 -1
- package/dist/server/analyze-job.js +14 -3
- package/dist/server/analyze-launch.d.ts +14 -0
- package/dist/server/analyze-launch.js +59 -14
- package/dist/server/analyze-worker-ipc.d.ts +6 -5
- package/dist/server/analyze-worker-ipc.js +4 -0
- package/dist/server/api.js +68 -21
- package/dist/server/git-clone.d.ts +38 -5
- package/dist/server/git-clone.js +216 -38
- package/dist/server/repo-projection.d.ts +88 -0
- package/dist/server/repo-projection.js +53 -0
- package/dist/storage/file-lock.js +5 -3
- package/dist/storage/parse-cache.js +32 -1
- package/dist/storage/parsedfile-store.js +53 -1
- package/dist/storage/v8-sidecar.d.ts +11 -0
- package/dist/storage/v8-sidecar.js +15 -7
- package/dist/utils/process-identity.d.ts +13 -0
- package/dist/utils/process-identity.js +18 -0
- package/package.json +1 -1
- package/scripts/build-tree-sitter-grammars.cjs +4 -3
- package/vendor/tree-sitter-objc/LICENSE +21 -0
- package/vendor/tree-sitter-objc/README.md +18 -0
- package/vendor/tree-sitter-objc/binding.gyp +35 -0
- package/vendor/tree-sitter-objc/bindings/node/binding.cc +19 -0
- package/vendor/tree-sitter-objc/bindings/node/binding_test.js +9 -0
- package/vendor/tree-sitter-objc/bindings/node/index.d.ts +27 -0
- package/vendor/tree-sitter-objc/bindings/node/index.js +11 -0
- package/vendor/tree-sitter-objc/grammar.js +1286 -0
- package/vendor/tree-sitter-objc/package.json +56 -0
- package/vendor/tree-sitter-objc/prebuilds/SHA256SUMS +6 -0
- package/vendor/tree-sitter-objc/prebuilds/darwin-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/darwin-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/linux-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/linux-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/win32-arm64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/prebuilds/win32-x64/tree-sitter-objc.node +0 -0
- package/vendor/tree-sitter-objc/queries/folds.scm +20 -0
- package/vendor/tree-sitter-objc/queries/highlights.scm +216 -0
- package/vendor/tree-sitter-objc/queries/indents.scm +1 -0
- package/vendor/tree-sitter-objc/queries/injections.scm +10 -0
- package/vendor/tree-sitter-objc/queries/locals.scm +1 -0
- package/vendor/tree-sitter-objc/src/grammar.json +16292 -0
- package/vendor/tree-sitter-objc/src/node-types.json +7524 -0
- package/vendor/tree-sitter-objc/src/parser.c +684023 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/alloc.h +54 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/array.h +291 -0
- package/vendor/tree-sitter-objc/src/tree_sitter/parser.h +266 -0
- package/vendor/tree-sitter-objc/tree-sitter-objc.wasm +0 -0
- package/vendor/tree-sitter-objc/tree-sitter.json +38 -0
- package/web/assets/{agent-BMRqXorA.js → agent-C2n33ANd.js} +1 -1
- package/web/assets/{index-Dj3vQOHK.js → index-CHJoW_P1.js} +18 -18
- package/web/assets/src-jc8Ffy3T.js +1 -0
- package/web/index.html +2 -2
- package/web/assets/src-Df5C1Nz4.js +0 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { SupportedLanguages, } from '../../../_shared/index.js';
|
|
3
|
+
import Parser from 'tree-sitter';
|
|
4
|
+
import { defineLanguage } from '../language-provider.js';
|
|
5
|
+
import { getLanguageGrammar } from '../../tree-sitter/parser-loader.js';
|
|
6
|
+
import { parseSourceSafe } from '../../tree-sitter/safe-parse.js';
|
|
7
|
+
import { assertCloneable } from '../workers/clone-safety.js';
|
|
8
|
+
import { preprocessObjectiveCMacroMarkers } from './objective-c/macro-marker-preprocess.js';
|
|
9
|
+
import { buildObjectiveCSemanticGraph, buildObjectiveCScopeCaptures, collectObjectiveCCaptureSideChannel, collectObjectiveCFacts, parseObjCType, setObjectiveCFileFacts, } from './objective-c/facts.js';
|
|
10
|
+
const OBJECTIVE_C_SCOPE_QUERY = `((translation_unit) @objc.root)`;
|
|
11
|
+
const EMPTY_TYPE_CONFIG = {
|
|
12
|
+
declarationNodeTypes: new Set(),
|
|
13
|
+
extractDeclaration: () => null,
|
|
14
|
+
extractParameter: () => null,
|
|
15
|
+
};
|
|
16
|
+
const noImportResolution = () => null;
|
|
17
|
+
function normalizedExt(filePath) {
|
|
18
|
+
return path.extname(filePath).toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function isObjectiveCSourcePath(filePath) {
|
|
21
|
+
const ext = normalizedExt(filePath);
|
|
22
|
+
return ext === '.m' || ext === '.mm';
|
|
23
|
+
}
|
|
24
|
+
function isHeaderPath(filePath) {
|
|
25
|
+
return normalizedExt(filePath) === '.h';
|
|
26
|
+
}
|
|
27
|
+
const OBJECTIVE_C_HEADER_NODE_TYPES = new Set([
|
|
28
|
+
'class_declaration',
|
|
29
|
+
'class_interface',
|
|
30
|
+
'class_implementation',
|
|
31
|
+
'compatibility_alias_declaration',
|
|
32
|
+
'module_import',
|
|
33
|
+
'protocol_declaration',
|
|
34
|
+
// Embedding chunks a Method node's snippet, not the enclosing @interface.
|
|
35
|
+
// tree-sitter-objc only emits these for +/- declarators, not C++ functions.
|
|
36
|
+
'method_declaration',
|
|
37
|
+
'method_definition',
|
|
38
|
+
]);
|
|
39
|
+
const OBJECTIVE_C_HEADER_DIRECTIVES = [
|
|
40
|
+
'@class',
|
|
41
|
+
'@compatibility_alias',
|
|
42
|
+
'@import',
|
|
43
|
+
'@implementation',
|
|
44
|
+
'@interface',
|
|
45
|
+
'@protocol',
|
|
46
|
+
];
|
|
47
|
+
function hasObjectiveCHeaderSyntax(sourceText) {
|
|
48
|
+
try {
|
|
49
|
+
const tree = parseObjectiveCSource(sourceText);
|
|
50
|
+
const stack = [tree.rootNode];
|
|
51
|
+
while (stack.length > 0) {
|
|
52
|
+
const node = stack.pop();
|
|
53
|
+
if (node === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
if (OBJECTIVE_C_HEADER_NODE_TYPES.has(node.type))
|
|
56
|
+
return true;
|
|
57
|
+
for (let i = node.namedChildCount - 1; i >= 0; i--) {
|
|
58
|
+
const child = node.namedChild(i);
|
|
59
|
+
if (child !== null)
|
|
60
|
+
stack.push(child);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Keep unambiguous Objective-C headers on the normal unavailable-parser path.
|
|
66
|
+
return hasObjectiveCHeaderDirective(sourceText) || hasObjectiveCMethodDeclarator(sourceText);
|
|
67
|
+
}
|
|
68
|
+
// Bare method snippets (`- (void)run;`) are not `method_declaration` at
|
|
69
|
+
// translation-unit scope; the grammar only emits that under @interface.
|
|
70
|
+
return hasObjectiveCMethodDeclarator(sourceText);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Line-leading `+/- (Type)selector` used when the tree has no @interface and
|
|
74
|
+
* when the grammar cannot load. `-(x);` does not match: `)` is not followed
|
|
75
|
+
* by a selector identifier.
|
|
76
|
+
*/
|
|
77
|
+
function hasObjectiveCMethodDeclarator(sourceText) {
|
|
78
|
+
return /(?:^|[\r\n])[ \t]*[+-][ \t]*\([^;\n]+\)[ \t]*[A-Za-z_]/.test(sourceTextWithoutComments(sourceText));
|
|
79
|
+
}
|
|
80
|
+
/** Replace comments with spaces so line-leading method probes see only code. */
|
|
81
|
+
function sourceTextWithoutComments(sourceText) {
|
|
82
|
+
const chars = sourceText.split('');
|
|
83
|
+
let index = 0;
|
|
84
|
+
let state = 'code';
|
|
85
|
+
while (index < chars.length) {
|
|
86
|
+
const current = chars[index];
|
|
87
|
+
const next = chars[index + 1];
|
|
88
|
+
if (state === 'line-comment') {
|
|
89
|
+
if (current === '\n' || current === '\r')
|
|
90
|
+
state = 'code';
|
|
91
|
+
else
|
|
92
|
+
chars[index] = ' ';
|
|
93
|
+
index++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (state === 'block-comment') {
|
|
97
|
+
if (current === '*' && next === '/') {
|
|
98
|
+
chars[index] = ' ';
|
|
99
|
+
chars[index + 1] = ' ';
|
|
100
|
+
state = 'code';
|
|
101
|
+
index += 2;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
if (current !== '\n' && current !== '\r')
|
|
105
|
+
chars[index] = ' ';
|
|
106
|
+
index++;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (state === 'single-quote' || state === 'double-quote') {
|
|
111
|
+
if (current === '\\') {
|
|
112
|
+
index += 2;
|
|
113
|
+
}
|
|
114
|
+
else if ((state === 'single-quote' && current === "'") ||
|
|
115
|
+
(state === 'double-quote' && current === '"')) {
|
|
116
|
+
state = 'code';
|
|
117
|
+
index++;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
index++;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (current === '/' && next === '/') {
|
|
125
|
+
chars[index] = ' ';
|
|
126
|
+
chars[index + 1] = ' ';
|
|
127
|
+
state = 'line-comment';
|
|
128
|
+
index += 2;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (current === '/' && next === '*') {
|
|
132
|
+
chars[index] = ' ';
|
|
133
|
+
chars[index + 1] = ' ';
|
|
134
|
+
state = 'block-comment';
|
|
135
|
+
index += 2;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (current === "'") {
|
|
139
|
+
state = 'single-quote';
|
|
140
|
+
index++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (current === '"') {
|
|
144
|
+
state = 'double-quote';
|
|
145
|
+
index++;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
index++;
|
|
149
|
+
}
|
|
150
|
+
return chars.join('');
|
|
151
|
+
}
|
|
152
|
+
function hasObjectiveCHeaderDirective(sourceText) {
|
|
153
|
+
let index = 0;
|
|
154
|
+
let state = 'code';
|
|
155
|
+
while (index < sourceText.length) {
|
|
156
|
+
const current = sourceText[index];
|
|
157
|
+
const next = sourceText[index + 1];
|
|
158
|
+
if (state === 'line-comment') {
|
|
159
|
+
if (current === '\n' || current === '\r')
|
|
160
|
+
state = 'code';
|
|
161
|
+
index++;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (state === 'block-comment') {
|
|
165
|
+
if (current === '*' && next === '/') {
|
|
166
|
+
state = 'code';
|
|
167
|
+
index += 2;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
index++;
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (state === 'single-quote' || state === 'double-quote') {
|
|
175
|
+
if (current === '\\') {
|
|
176
|
+
index += 2;
|
|
177
|
+
}
|
|
178
|
+
else if ((state === 'single-quote' && current === "'") ||
|
|
179
|
+
(state === 'double-quote' && current === '"')) {
|
|
180
|
+
state = 'code';
|
|
181
|
+
index++;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
index++;
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (current === '/' && next === '/') {
|
|
189
|
+
state = 'line-comment';
|
|
190
|
+
index += 2;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (current === '/' && next === '*') {
|
|
194
|
+
state = 'block-comment';
|
|
195
|
+
index += 2;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (current === "'") {
|
|
199
|
+
state = 'single-quote';
|
|
200
|
+
index++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (current === '"') {
|
|
204
|
+
state = 'double-quote';
|
|
205
|
+
index++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (current === '@') {
|
|
209
|
+
const directive = OBJECTIVE_C_HEADER_DIRECTIVES.find((candidate) => sourceText.startsWith(candidate, index));
|
|
210
|
+
if (directive !== undefined && !isIdentifierCharacter(sourceText[index + directive.length])) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
index++;
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
function isIdentifierCharacter(character) {
|
|
219
|
+
return character !== undefined && /[A-Za-z0-9_]/.test(character);
|
|
220
|
+
}
|
|
221
|
+
export function classifyObjectiveCFileContent(filePath, sourceText) {
|
|
222
|
+
if (isObjectiveCSourcePath(filePath))
|
|
223
|
+
return true;
|
|
224
|
+
if (!isHeaderPath(filePath))
|
|
225
|
+
return false;
|
|
226
|
+
return hasObjectiveCHeaderSyntax(sourceText);
|
|
227
|
+
}
|
|
228
|
+
function parseObjectiveCSource(sourceText) {
|
|
229
|
+
const parser = new Parser();
|
|
230
|
+
parser.setLanguage(getLanguageGrammar(SupportedLanguages.ObjectiveC));
|
|
231
|
+
return parseSourceSafe(parser, preprocessObjectiveCMacroMarkers(sourceText), undefined, undefined, 'Objective-C source');
|
|
232
|
+
}
|
|
233
|
+
function treeFromCachedOrSource(cachedTree, sourceText) {
|
|
234
|
+
if (cachedTree !== undefined && looksLikeTree(cachedTree))
|
|
235
|
+
return cachedTree;
|
|
236
|
+
return parseObjectiveCSource(sourceText);
|
|
237
|
+
}
|
|
238
|
+
function looksLikeTree(value) {
|
|
239
|
+
return (value !== null &&
|
|
240
|
+
typeof value === 'object' &&
|
|
241
|
+
'rootNode' in value &&
|
|
242
|
+
value.rootNode !== undefined);
|
|
243
|
+
}
|
|
244
|
+
function interpretObjectiveCImport(captures) {
|
|
245
|
+
const source = captures['@import.source'];
|
|
246
|
+
if (source === undefined || source.text.trim().length === 0)
|
|
247
|
+
return null;
|
|
248
|
+
const targetRaw = source.text.trim();
|
|
249
|
+
const kind = captures['@import.kind']?.text.trim();
|
|
250
|
+
const isSystemHeader = targetRaw.startsWith('<') && targetRaw.endsWith('>');
|
|
251
|
+
return {
|
|
252
|
+
kind: 'side-effect',
|
|
253
|
+
// Scope resolution needs to distinguish a quoted header path from a bare
|
|
254
|
+
// @import module name and an angle-bracket system header. The latter stays
|
|
255
|
+
// wrapped so the Objective-C resolver can fail closed instead of resolving
|
|
256
|
+
// a framework header to a same-named local file.
|
|
257
|
+
targetRaw: isSystemHeader || kind === 'module' || targetRaw.startsWith('./')
|
|
258
|
+
? targetRaw
|
|
259
|
+
: `./${targetRaw}`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function interpretObjectiveCTypeBinding(captures) {
|
|
263
|
+
const name = captures['@type-binding.name'];
|
|
264
|
+
const type = captures['@type-binding.type'];
|
|
265
|
+
if (name === undefined || type === undefined)
|
|
266
|
+
return null;
|
|
267
|
+
const parsed = parseObjCType(type.text);
|
|
268
|
+
return {
|
|
269
|
+
boundName: name.text,
|
|
270
|
+
rawTypeName: parsed?.name ?? parsed?.raw ?? type.text,
|
|
271
|
+
declaredSpelling: type.text,
|
|
272
|
+
source: 'annotation',
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
export const objectiveCProvider = defineLanguage({
|
|
276
|
+
id: SupportedLanguages.ObjectiveC,
|
|
277
|
+
extensions: ['.m', '.mm'],
|
|
278
|
+
treeSitterQueries: OBJECTIVE_C_SCOPE_QUERY,
|
|
279
|
+
typeConfig: EMPTY_TYPE_CONFIG,
|
|
280
|
+
exportChecker: () => true,
|
|
281
|
+
importResolver: noImportResolution,
|
|
282
|
+
classifyFileContent: classifyObjectiveCFileContent,
|
|
283
|
+
shouldClassifyFileContent: isHeaderPath,
|
|
284
|
+
preprocessSource: preprocessObjectiveCMacroMarkers,
|
|
285
|
+
importsExecuteWhereWritten: false,
|
|
286
|
+
emitScopeCaptures: (sourceText, filePath, cachedTree) => {
|
|
287
|
+
const tree = treeFromCachedOrSource(cachedTree, sourceText);
|
|
288
|
+
const facts = collectObjectiveCFacts(tree, filePath);
|
|
289
|
+
setObjectiveCFileFacts(facts);
|
|
290
|
+
return buildObjectiveCScopeCaptures(facts, tree.rootNode);
|
|
291
|
+
},
|
|
292
|
+
collectCaptureSideChannel: (filePath) => assertCloneable(collectObjectiveCCaptureSideChannel(filePath)),
|
|
293
|
+
interpretImport: interpretObjectiveCImport,
|
|
294
|
+
interpretTypeBinding: interpretObjectiveCTypeBinding,
|
|
295
|
+
extractSemanticGraph: (tree, filePath) => {
|
|
296
|
+
const facts = collectObjectiveCFacts(tree, filePath);
|
|
297
|
+
setObjectiveCFileFacts(facts);
|
|
298
|
+
return buildObjectiveCSemanticGraph(facts);
|
|
299
|
+
},
|
|
300
|
+
});
|
|
@@ -51,6 +51,37 @@
|
|
|
51
51
|
*/
|
|
52
52
|
import Parser from 'tree-sitter';
|
|
53
53
|
export declare const TYPESCRIPT_SCOPE_QUERY: string;
|
|
54
|
+
/**
|
|
55
|
+
* JSX-only query suffix. Appended to the base query when compiling
|
|
56
|
+
* against the TSX grammar; NOT compiled against the plain TS grammar
|
|
57
|
+
* (which has no \`jsx_*\` node types and would reject these patterns).
|
|
58
|
+
*
|
|
59
|
+
* Why JSX as a CALLS edge: \`<Foo />\` is syntactic sugar for \`Foo(props)\`
|
|
60
|
+
* and the React component is invoked by the renderer, so for blast-radius
|
|
61
|
+
* (\`gitnexus_impact("Badge", direction: "upstream")\`) and call-graph
|
|
62
|
+
* (\`gitnexus_context("Foo")\`) purposes JSX usage IS a call. Routing
|
|
63
|
+
* through \`@reference.call.free\` / \`@reference.call.member\` makes the
|
|
64
|
+
* downstream caller-walk + edge-emission paths handle JSX uniformly with
|
|
65
|
+
* ordinary call expressions — no new edge type, no schema changes.
|
|
66
|
+
*
|
|
67
|
+
* Identifier-only JSX is filtered to PascalCase via \`(#match? ... "^[A-Z]")\`
|
|
68
|
+
* so \`<div>\`, \`<span>\`, \`<button>\` and other native HTML elements (which
|
|
69
|
+
* by JSX convention start lowercase) don't emit edges to nonexistent
|
|
70
|
+
* "div" / "span" symbols. Member-form JSX (\`<Foo.Bar />\`) is always a
|
|
71
|
+
* component (HTML element names can't contain dots), so no predicate
|
|
72
|
+
* filter is applied there.
|
|
73
|
+
*
|
|
74
|
+
* Both \`jsx_self_closing_element\` (\`<Foo />\`) and \`jsx_opening_element\`
|
|
75
|
+
* (\`<Foo>...</Foo>\`) emit; the closing tag is intentionally NOT captured —
|
|
76
|
+
* each JSX element should emit exactly one CALLS edge per use site.
|
|
77
|
+
*/
|
|
78
|
+
/**
|
|
79
|
+
* Exported alongside `TYPESCRIPT_SCOPE_QUERY` so
|
|
80
|
+
* `value-ref-dispatchability.test.ts` checks the whole query a `.tsx` file is
|
|
81
|
+
* analyzed with. Checking the base alone would miss a `value-ref` rule added
|
|
82
|
+
* here. Not part of the provider surface — use `getTsScopeQuery`.
|
|
83
|
+
*/
|
|
84
|
+
export declare const TSX_JSX_QUERY_SUFFIX = "\n;; <Foo />\n((jsx_self_closing_element\n name: (identifier) @reference.name) @reference.call.free\n (#match? @reference.name \"^[A-Z]\"))\n\n;; <Foo> ... </Foo> (paired form \u2014 match the opening tag only)\n((jsx_opening_element\n name: (identifier) @reference.name) @reference.call.free\n (#match? @reference.name \"^[A-Z]\"))\n\n;; <Foo.Bar /> / <Container.Section.Title /> \u2014 namespaced JSX\n(jsx_self_closing_element\n name: (member_expression\n object: (_) @reference.receiver\n property: (property_identifier) @reference.name)) @reference.call.member\n\n(jsx_opening_element\n name: (member_expression\n object: (_) @reference.receiver\n property: (property_identifier) @reference.name)) @reference.call.member\n";
|
|
54
85
|
/**
|
|
55
86
|
* Return the right tree-sitter parser for `filePath` (or the TS parser
|
|
56
87
|
* when no path is given — the legacy callsite shape).
|
|
@@ -1338,7 +1338,13 @@ export const TYPESCRIPT_SCOPE_QUERY = `
|
|
|
1338
1338
|
* (\`<Foo>...</Foo>\`) emit; the closing tag is intentionally NOT captured —
|
|
1339
1339
|
* each JSX element should emit exactly one CALLS edge per use site.
|
|
1340
1340
|
*/
|
|
1341
|
-
|
|
1341
|
+
/**
|
|
1342
|
+
* Exported alongside `TYPESCRIPT_SCOPE_QUERY` so
|
|
1343
|
+
* `value-ref-dispatchability.test.ts` checks the whole query a `.tsx` file is
|
|
1344
|
+
* analyzed with. Checking the base alone would miss a `value-ref` rule added
|
|
1345
|
+
* here. Not part of the provider surface — use `getTsScopeQuery`.
|
|
1346
|
+
*/
|
|
1347
|
+
export const TSX_JSX_QUERY_SUFFIX = `
|
|
1342
1348
|
;; <Foo />
|
|
1343
1349
|
((jsx_self_closing_element
|
|
1344
1350
|
name: (identifier) @reference.name) @reference.call.free
|
|
@@ -234,9 +234,10 @@ async function firstReadableConfig(base) {
|
|
|
234
234
|
async function findTsconfigFiles(repoRoot) {
|
|
235
235
|
const found = [];
|
|
236
236
|
const queue = [{ dir: repoRoot, depth: 0 }];
|
|
237
|
+
let queueHead = 0;
|
|
237
238
|
let dirsScanned = 0;
|
|
238
|
-
while (queue.length
|
|
239
|
-
const { dir, depth } = queue
|
|
239
|
+
while (queueHead < queue.length && dirsScanned < SCAN_MAX_DIRS) {
|
|
240
|
+
const { dir, depth } = queue[queueHead++];
|
|
240
241
|
dirsScanned++;
|
|
241
242
|
let entries;
|
|
242
243
|
try {
|
|
@@ -314,7 +314,7 @@ const TS_OWNED_RECEIVERS = new Set(['this']);
|
|
|
314
314
|
const tsScopeOwnsReceivers = (match) => match['@receiver-owner.this'] === undefined ? undefined : TS_OWNED_RECEIVERS;
|
|
315
315
|
export const typescriptProvider = defineLanguage({
|
|
316
316
|
id: SupportedLanguages.TypeScript,
|
|
317
|
-
extensions: ['.ts', '.tsx'],
|
|
317
|
+
extensions: ['.ts', '.tsx', '.mts', '.cts'],
|
|
318
318
|
entryPointPatterns: [/^use[A-Z]/],
|
|
319
319
|
astFrameworkPatterns: [
|
|
320
320
|
{
|
|
@@ -1,3 +1,29 @@
|
|
|
1
1
|
import Parser from 'tree-sitter';
|
|
2
|
+
/**
|
|
3
|
+
* Zig scope-resolution query (RFC #909 Ring 3).
|
|
4
|
+
*
|
|
5
|
+
* The grammar is vendored (`vendor/tree-sitter-zig`) and may be absent on a
|
|
6
|
+
* platform without a prebuild, so the language module is required lazily and
|
|
7
|
+
* `getZigParser` / `getZigScopeQuery` throw only when actually invoked without
|
|
8
|
+
* the grammar installed. That is safe: the parse pipeline filters `.zig` files
|
|
9
|
+
* through `parser-loader.isLanguageAvailable` before any scope extraction runs.
|
|
10
|
+
*
|
|
11
|
+
* Zig specifics encoded here:
|
|
12
|
+
* - Containers (struct/enum/union/opaque) are anonymous nodes bound by the
|
|
13
|
+
* enclosing `variable_declaration`; declarations capture the binding
|
|
14
|
+
* identifier from the wrapper.
|
|
15
|
+
* - `@import` is a builtin call, not import-statement syntax; the
|
|
16
|
+
* `#eq?` predicate keeps other builtins (@sizeOf, @as, …) out.
|
|
17
|
+
* - A plain `(variable_declaration (identifier))` rule would also match
|
|
18
|
+
* container and import bindings — `emitZigScopeCaptures` filters those
|
|
19
|
+
* groups out so a name binds exactly once.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Exported for `value-ref-dispatchability.test.ts`, which reads every language's
|
|
23
|
+
* scope query to enforce the keyed/unkeyed partition that
|
|
24
|
+
* `callableValueReferenceBoundaries`' dispatch exclusion depends on. Not part of
|
|
25
|
+
* the provider surface — nothing else should import it.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ZIG_SCOPE_QUERY = "\n;; Scopes\n(source_file) @scope.module\n(struct_declaration) @scope.class\n(enum_declaration) @scope.class\n(union_declaration) @scope.class\n(opaque_declaration) @scope.class\n(function_declaration) @scope.function\n(test_declaration) @scope.function\n(block) @scope.block\n\n;; Declarations \u2014 functions (relabeled @declaration.method inside containers\n;; by emitZigScopeCaptures, mirroring the provider's labelOverride)\n(function_declaration\n name: (identifier) @declaration.name) @declaration.function\n\n;; Declarations \u2014 named tests. Same naming rule as ZIG_QUERIES: the string\n;; node WITH quotes, so the def joins the graph node and never collides with\n;; a same-named fn. Anonymous / decl-form tests are scopes without a def.\n(test_declaration\n (string) @declaration.name) @declaration.function\n\n;; Declarations \u2014 containers. The binding name lives on the wrapper\n;; variable_declaration, but the ANCHOR is the container node itself so its\n;; range equals the @scope.class range: the extractor then attaches the def\n;; to the class scope (walkers.populateClassOwnedMembers expects the\n;; class-like def among the class scope's ownedDefs) and auto-hoists the\n;; name binding to the parent scope. Keyword-gated like the ordinary\n;; binding rules: a keyword-less `x = struct {\u2026};` is an assignment\n;; (tree-sitter-zig 1.1.2 reuses variable_declaration), not a container def.\n(variable_declaration\n \"const\" . (identifier) @declaration.name\n (struct_declaration) @declaration.struct)\n(variable_declaration\n \"var\" . (identifier) @declaration.name\n (struct_declaration) @declaration.struct)\n(variable_declaration\n \"const\" . (identifier) @declaration.name\n (enum_declaration) @declaration.enum)\n(variable_declaration\n \"var\" . (identifier) @declaration.name\n (enum_declaration) @declaration.enum)\n(variable_declaration\n \"const\" . (identifier) @declaration.name\n (union_declaration) @declaration.union)\n(variable_declaration\n \"var\" . (identifier) @declaration.name\n (union_declaration) @declaration.union)\n;; opaque {} is a fieldless container that may own methods \u2014 Struct, as in\n;; ZIG_QUERIES (see the rationale there).\n(variable_declaration\n \"const\" . (identifier) @declaration.name\n (opaque_declaration) @declaration.struct)\n(variable_declaration\n \"var\" . (identifier) @declaration.name\n (opaque_declaration) @declaration.struct)\n\n;; Declarations \u2014 generic type constructors. `fn List(comptime T: type) type\n;; { return struct {\u2026}; }` is Zig's only spelling of a generic type; the\n;; returned container is anonymous in the grammar but every reader (and every\n;; caller: `List(u8)`) names it after the function. Anchor on the container\n;; so the def sits in its own class scope; the name binding is hoisted to the\n;; MODULE scope by `zigBindingScopeFor` (not the fn body, where the name\n;; would be invisible to callers) and coexists with the Function def of the\n;; same name \u2014 `List` really is both a callable and a type.\n((function_declaration\n name: (identifier) @declaration.name\n type: (builtin_type) @_ret\n body: (block (expression_statement (return_expression\n (struct_declaration) @declaration.struct))))\n (#eq? @_ret \"type\"))\n((function_declaration\n name: (identifier) @declaration.name\n type: (builtin_type) @_ret\n body: (block (expression_statement (return_expression\n (union_declaration) @declaration.union))))\n (#eq? @_ret \"type\"))\n((function_declaration\n name: (identifier) @declaration.name\n type: (builtin_type) @_ret\n body: (block (expression_statement (return_expression\n (enum_declaration) @declaration.enum))))\n (#eq? @_ret \"type\"))\n\n;; Declarations \u2014 container fields (struct fields, enum/union variants).\n;; The #not-eq? guard drops the MISSING placeholder identifier tree-sitter-zig\n;; recovers for an empty container body (see ZIG_QUERIES). The optional\n;; `type:` (absent on enum variants) is captured as @declaration.field-type:\n;; `emitZigScopeCaptures` turns it into a @type-binding.field on the\n;; container's Class scope so `self.session.name()` can walk the field's\n;; type (Rust/Go parity \u2014 see the F5 block in captures.ts).\n((container_field\n name: (identifier) @declaration.name\n type: (_)? @declaration.field-type) @declaration.field\n (#not-eq? @declaration.name \"\"))\n\n;; Declarations \u2014 const/var bindings (import/container groups filtered in TS).\n;; The `.` anchor pins the FIRST named child: without it the pattern also\n;; matched the initializer of `const first = target;`, minting a phantom\n;; local named `target` that shadowed the real callee for every later\n;; reference in the block. The literal keyword is load-bearing too:\n;; tree-sitter-zig 1.1.2 parses statement assignments (`x = 5;`, `x += 1;`,\n;; `_ = expr;`) as `variable_declaration` WITHOUT a keyword child, and\n;; without the keyword every assignment and every discard minted a phantom\n;; local (one `_` per statement).\n(variable_declaration\n \"const\" . (identifier) @declaration.name) @declaration.variable\n(variable_declaration\n \"var\" . (identifier) @declaration.name) @declaration.variable\n\n;; Imports \u2014 const x = @import(\"...\") / var x = @import(\"...\"). Keyword-gated\n;; like every binding rule: a keyword-less `x = @import(\"...\")` is a\n;; statement (see the side-effect rule below), not a binding.\n(variable_declaration\n \"const\" . (identifier) @import.name\n (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n (#eq? @_builtin \"@import\")) @import.statement\n(variable_declaration\n \"var\" . (identifier) @import.name\n (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n (#eq? @_builtin \"@import\")) @import.statement\n\n;; Imports \u2014 const X = @import(\"...\").X : a NAMED import of one member. The\n;; local name is whatever the user chose (`const Alloc = @import(\"std\").mem;`\n;; is a rename), the imported name is the member. A deeper chain\n;; (`@import(\"std\").mem.Allocator`, `@import(\"lib.zig\").B.work`) is matched\n;; by the second rule below but is NOT bound as a named import of the\n;; innermost member: that discarded the written owner (`B`) and let a\n;; same-named `A.work` answer first. `emitZigScopeCaptures` binds the module\n;; under the builtin's text instead and rewrites the alias's use sites to the\n;; full path (`collectZigDeepAliases`, PR #1432 review 8.4).\n(variable_declaration\n \"const\" . (identifier) @import.name\n (field_expression\n object: (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n member: (identifier) @import.imported)\n (#eq? @_builtin \"@import\")) @import.statement\n(variable_declaration\n \"const\" . (identifier) @import.name\n (field_expression\n object: (field_expression\n object: (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source)))\n member: (identifier) @import.imported)\n (#eq? @_builtin \"@import\")) @import.statement\n\n;; Imports \u2014 a keyword-less `<ident> = @import(\"...\");` statement\n;; (`_ = @import(\"all_tests.zig\");` in a test block, the refAllDecls\n;; idiom): tree-sitter-zig reuses `variable_declaration` for assignments, so\n;; the shape is a declaration minus the keyword. It references the file\n;; without binding a name \u2014 a side-effect import. Tree-sitter queries cannot\n;; say \"no keyword child\", so this rule matches the keyword-bearing shapes\n;; too; `emitZigScopeCaptures` keeps it only when `isZigKeywordDeclaration`\n;; is false (the keyword shapes are the binding rules above).\n(variable_declaration\n . (identifier)\n (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n (#eq? @_builtin \"@import\")) @import.side-effect\n\n;; Aliases of a namespace member \u2014 const Counter = counter.Counter; where\n;; `counter` is an @import binding of THIS file. The query cannot know which\n;; identifiers are import bindings, so it captures every one-level member\n;; alias and `emitZigScopeCaptures` promotes the ones whose object is a\n;; known @import to a named import (same fact as `const Counter =\n;; @import(\"counter.zig\").Counter;`); the rest stay ordinary variables.\n;; Only the ONE-level shape is promoted; a deeper chain (`lib.B.work`,\n;; `std.mem.Allocator`) is a deep alias \u2014 a Const whose use sites are\n;; rewritten to the written owner path (see the import rule above, 8.4).\n(variable_declaration\n \"const\" . (identifier) @alias.name\n (field_expression\n object: (identifier) @alias.namespace\n member: (identifier) @alias.member) .) @alias.statement\n(variable_declaration\n \"const\" . (identifier) @alias.name\n (field_expression\n object: (field_expression\n object: (identifier) @alias.namespace)\n member: (identifier) @alias.member) .) @alias.statement\n\n;; Imports \u2014 pub usingnamespace @import(\"...\"); : every pub decl of the target\n;; becomes a decl of this container (removed from the language in 0.15, still\n;; everywhere in 0.11\u20130.14 code). Modelled as a wildcard import.\n(using_namespace_declaration\n (builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n (#eq? @_builtin \"@import\")) @import.wildcard\n\n;; Imports \u2014 `@import(\"...\")` in ANY other position: a tuple element\n;; (`pub const Interfaces = .{ @import(\"a.zig\"), @import(\"b.zig\") }`, the\n;; JS-API registration table), a call argument (`event.is(@import(\"x.zig\"))`),\n;; a comparison operand (`T == @import(\"x.zig\").T`), the receiver of a\n;; member call (`try @import(\"dump.zig\").root(...)`), a 3-deep member chain\u2026\n;; Every one of them is a file dependency; only the const/var/usingnamespace\n;; shapes above bind a name. This rule matches EVERY `@import` builtin, the\n;; bound shapes included \u2014 `emitZigScopeCaptures` drops the matches whose\n;; string node a binding rule (or the keyword-less side-effect rule) already\n;; claimed, so a bound import is never doubled, and emits the rest as\n;; side-effect imports (file edge, no binding) \u2014 except the member-call\n;; receiver, which becomes a namespace import keyed by its own source text so\n;; the call resolves into the imported module (see the emitter).\n((builtin_function\n (builtin_identifier) @_builtin\n (arguments (string) @import.source))\n (#eq? @_builtin \"@import\")) @import.inline\n\n;; Type bindings \u2014 parameter annotations (incl. self: *T receivers)\n(parameter\n name: (identifier) @type-binding.name\n type: (_) @type-binding.type) @type-binding.parameter\n\n;; Type bindings \u2014 constructor inference: const p = T{ ... }. Keyword-gated\n;; like every binding rule: a keyword-less `p = T{ ... };` is a\n;; re-assignment (same node type in tree-sitter-zig 1.1.2), and Zig's static\n;; typing means `p` already carries its type from its declaration\n;; (annotation, constructor or inferred value) \u2014 the assignment declares\n;; nothing, and `_ = T{ ... };` must not bind `_`.\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (struct_initializer\n (identifier) @type-binding.type)) @type-binding.constructor\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (struct_initializer\n (identifier) @type-binding.type)) @type-binding.constructor\n\n;; Type bindings \u2014 qualified constructor: const p = mod.T{ ... }. The whole\n;; field_expression is captured so the dotted text \"mod.T\" survives \u2014\n;; receiver dispatch resolves the namespace prefix through the import\n;; binding (emitReceiverBoundCalls Case 3).\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (struct_initializer\n (field_expression) @type-binding.type)) @type-binding.constructor\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (struct_initializer\n (field_expression) @type-binding.type)) @type-binding.constructor\n\n;; Type bindings \u2014 generic instantiation literal: const l = List(u8){ ... }.\n;; The callee is the type constructor; `normalizeZigTypeName` drops the\n;; comptime argument list so `List(u8)` looks up `List`.\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (struct_initializer\n (call_expression) @type-binding.type)) @type-binding.constructor\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (struct_initializer\n (call_expression) @type-binding.type)) @type-binding.constructor\n\n;; Type bindings \u2014 declared type: var x: T = \u2026; const x: T = .init(\u2026);\n;; The annotation is the ONLY type source for `= undefined` and for 0.14+\n;; decl literals (`.init`, `.empty`), which are the idiomatic\n;; constructors in current std. Ranked below constructor inference by the\n;; shared resolver (source 'annotation'), so a literal on the right still\n;; wins when both are present.\n(variable_declaration\n . (identifier) @type-binding.name\n type: (_) @type-binding.type) @type-binding.annotation\n\n;; Type bindings \u2014 value inference (F6): const t = <value>; where <value> is a\n;; call (`Counter.init()`, `makeThing()`, `node.asElement()`), possibly\n;; wrapped in `try` / `catch \u2026` / `\u2026 orelse \u2026` / parentheses \u2014 the shape of\n;; nearly every Zig constructor call (`const p = try Page.init(\u2026)`). The\n;; query only pins the declaration; `emitZigScopeCaptures` unwraps the\n;; wrappers and rewrites `@type-binding.type` to the type source (see\n;; `zigCallReturnTypeOf`). Keyword-gated: `_ = e.top();` is an assignment\n;; (same node type), not a binding of `_`. Rust's twin is\n;; `let x = Foo::new()` / `let x = foo().await`.\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (_) @type-binding.value .) @type-binding.call-return\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (_) @type-binding.value .) @type-binding.call-return\n\n;; Type bindings \u2014 return-type annotation (F6): `fn make() !*Thing` binds\n;; `make \u21A6 Thing` in the enclosing scope (Module for free fns, the container's\n;; Class scope for methods \u2014 that is where the compound resolver reads a\n;; method's return type for `node.asElement()`), so `const t = makeThing()`\n;; chains to `Thing`. Rust: `(function_item \u2026 return_type:) @type-binding.return`.\n;; `emitZigScopeCaptures` drops builtin / `type` returns (a `List \u21A6 type`\n;; binding would hijack the `List(u8){}` constructor chain).\n(function_declaration\n name: (identifier) @type-binding.name\n type: (_) @type-binding.type) @type-binding.return\n\n;; Type bindings \u2014 aliases (F5 field-access aliases + F7 type aliases):\n;; `const page = self.page;` (F5: the RHS path is kept verbatim as the\n;; \"type\", the compound resolver's member-alias branch re-resolves it as a\n;; receiver chain \u2014 head `self` \u2192 class \u2192 field type), `const LocalAlias = Local;`,\n;; `const Proto = HtmlElement;`, `const T2 = Thing;` (alias of an alias /\n;; import), `const B = util.List(u8);` (an INSTANTIATED generic type\n;; constructor). Zig has no `type X = Y` syntax \u2014 a type alias is a const\n;; whose value is a type expression, and it stays a Const in the graph. What\n;; must change is the scope side: bind the alias NAME to the value's type\n;; text (Rust's `let x = y` / JS's `const B = Foo` `@type-binding.alias`,\n;; source 'assignment-inferred'), so `LocalAlias.mk()` types through Case 4,\n;; `B.init()` / `x: B` / `B{}` through Case 3 once `normalizeZigTypeName`\n;; drops the comptime arguments (`util.List(u8)` \u2192 `util.List`), and every\n;; binding that names the alias (`var l = LocalAlias.mk()`, `var x: B`) is\n;; chained to the target by the shared `followChainedRef` /\n;; `followChainPostFinalize`. The identifier / member shapes take `var` too:\n;; `var node = orig_node;` is the same value alias as Rust's `let x = y`\n;; and chains to the type of `orig_node` (a Zig type is comptime and never\n;; `var`, so the type-alias reading only ever applies to `const`). The\n;; call shape is `const`-only and kept only when the callee's last\n;; identifier is TitleCase \u2014 see `emitZigScopeCaptures` (a value call\n;; `const t = util.makeThing()` belongs to the call-return rules above and\n;; must not receive a competing binding). A promoted namespace-member alias\n;; (`const Counter = counter.Counter;` \u2192 named import) is skipped there too:\n;; the import binding already carries the type.\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (identifier) @type-binding.type .) @type-binding.alias\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (identifier) @type-binding.type .) @type-binding.alias\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (field_expression) @type-binding.type .) @type-binding.alias\n(variable_declaration\n \"var\" . (identifier) @type-binding.name\n (field_expression) @type-binding.type .) @type-binding.alias\n(variable_declaration\n \"const\" . (identifier) @type-binding.name\n (call_expression) @type-binding.type .) @type-binding.alias\n\n;; References \u2014 VALUE positions (#3399): a callable named where a value is\n;; expected rather than where a callee is. Zig's JS bridge is built entirely\n;; out of this shape \u2014\n;;\n;; pub const namespaceURI = bridge.accessor(Element.getNamespaceUri, null, .{});\n;;\n;; \u2014 2,047 such declarations across 257 files in lightpanda-io/browser, the\n;; project's whole JS\u2194Zig surface, and NONE of them reached the graph: Zig\n;; emitted no `value-ref` capture at all, so a public DOM accessor's only\n;; recorded callers were the two internal ones and `impact` called that `exact`.\n;;\n;; These become reference-class USES edges through the existing\n;; `mapReferenceKindToEdgeType` mapping \u2014 a registration is not an invocation\n;; (Kythe `ref` vs `ref/call`; Joern `METHOD_REF`) \u2014 resolved by the\n;; property-dispatch pass, which keeps ONLY callable targets. That callable gate\n;; is what makes these deliberately broad rules safe: `js.Bridge(Element)` and\n;; `register(count)` match too, and emit nothing, exactly as TypeScript's\n;; `{ port: DEFAULT_PORT }` does.\n;;\n;; No `@reference.property-key` is attached: Zig has no object-literal key to\n;; dispatch through, so these register a reference and never synthesize CALLS.\n;; The terminal invoke \u2014 `Accessor.init` \u2192 a struct field \u2192 `Factory.zig`'s\n;; `inline for`/`@typeInfo` \u2192 `@call(.auto, func, args)` \u2014 needs comptime\n;; evaluation and is deliberately NOT modelled; `impact` reports the shortfall\n;; as `epistemic: \"lower-bound\"` instead of pretending to certainty.\n\n;; Call ARGUMENTS. In tree-sitter-zig arguments are direct children of\n;; `call_expression`, NOT wrapped in an `arguments` node (only builtins have\n;; one), so the callee has to be consumed explicitly by `function:` \u2014 without\n;; that binding the same rule also matches the callee of `foo(bar)` and mints a\n;; USES edge duplicating the call.\n(call_expression\n function: (_)\n (identifier) @reference.name @reference.value-ref)\n\n;; Qualified argument \u2014 `bridge.accessor(Element.getNamespaceUri, \u2026)`. The\n;; RECEIVER is captured alongside the member so the site carries the owner it\n;; was written with; `@reference.name` stays the member, which is the name the\n;; scope walk resolves.\n(call_expression\n function: (_)\n (field_expression\n object: (_) @reference.receiver\n member: (identifier) @reference.name) @reference.value-ref)\n\n;; Const binding initialiser \u2014 `pub const defaultHandler = onReset;`. Both\n;; anchors are load-bearing: the leading `.` pins the bound name to the first\n;; named child (see the declaration rules above), and the trailing `.` keeps the\n;; initializer as the LAST child, so a `const x: T = y` annotation shape cannot\n;; put the TYPE in value position.\n(variable_declaration\n \"const\" . (identifier)\n (identifier) @reference.name @reference.value-ref .)\n\n;; References \u2014 free calls: foo(...)\n(call_expression\n function: (identifier) @reference.name) @reference.call.free\n\n;; References \u2014 member calls: obj.method(...) / mod.fn(...)\n(call_expression\n function: (field_expression\n object: (_) @reference.receiver\n member: (identifier) @reference.name)) @reference.call.member\n\n;; References \u2014 constructor uses: T{ ... }\n(struct_initializer\n (identifier) @reference.name) @reference.call.constructor\n\n;; References \u2014 qualified constructor uses: mod.T{ ... } / hub.sub.T{ ... }\n;; Captured with the RECEIVER, not as a free constructor with a raw qualified\n;; name, on purpose: the free-call fallback resolves a qualified constructor by\n;; its simple tail, and a workspace-unique `Thing` then answers for\n;; `other.Thing{}` whichever module the source named (measured: `c.Thing{}`\n;; with no `Thing` in c.zig bound to a.zig's). With the receiver the site goes\n;; through the receiver-bound namespace case, which resolves the member inside\n;; the module the receiver is bound to \u2014 the same path `mod.fn()` takes \u2014 so\n;; `a.Thing{}` and `b.Thing{}` each bind their own file and `std.Thread.Mutex{}`\n;; binds nothing even when a local `Mutex` exists.\n(struct_initializer\n (field_expression\n object: (_) @reference.receiver\n member: (identifier) @reference.name)) @reference.call.constructor\n\n;; References \u2014 generic instantiation literals: List(u8){ ... } /\n;; lists.List(u8){ ... }. The type head is a call_expression \u2014 the\n;; instantiation of the type constructor \u2014 which neither constructor rule\n;; above matches, so the OUTER aggregate event had no site: only the inner\n;; `List(u8)` call (a free / member call reference on the call node) reached\n;; the graph (PR #1432 review, 8.11). Two sites on two anchors: the call\n;; (an invocation of `List`) and this initializer (a construction of the\n;; container `List` returns, marked `(constructor)`). The receiver form goes\n;; through the same namespace path as `mod.T{}`.\n(struct_initializer\n (call_expression\n function: (identifier) @reference.name)) @reference.call.constructor\n(struct_initializer\n (call_expression\n function: (field_expression\n object: (_) @reference.receiver\n member: (identifier) @reference.name))) @reference.call.constructor\n";
|
|
2
28
|
export declare function getZigParser(): Parser;
|
|
3
29
|
export declare function getZigScopeQuery(): Parser.Query;
|
|
@@ -19,7 +19,13 @@ import { requireVendoredGrammar } from '../../../tree-sitter/vendored-grammars.j
|
|
|
19
19
|
* container and import bindings — `emitZigScopeCaptures` filters those
|
|
20
20
|
* groups out so a name binds exactly once.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Exported for `value-ref-dispatchability.test.ts`, which reads every language's
|
|
24
|
+
* scope query to enforce the keyed/unkeyed partition that
|
|
25
|
+
* `callableValueReferenceBoundaries`' dispatch exclusion depends on. Not part of
|
|
26
|
+
* the provider surface — nothing else should import it.
|
|
27
|
+
*/
|
|
28
|
+
export const ZIG_SCOPE_QUERY = `
|
|
23
29
|
;; Scopes
|
|
24
30
|
(source_file) @scope.module
|
|
25
31
|
(struct_declaration) @scope.class
|
|
@@ -358,6 +364,60 @@ const ZIG_SCOPE_QUERY = `
|
|
|
358
364
|
"const" . (identifier) @type-binding.name
|
|
359
365
|
(call_expression) @type-binding.type .) @type-binding.alias
|
|
360
366
|
|
|
367
|
+
;; References — VALUE positions (#3399): a callable named where a value is
|
|
368
|
+
;; expected rather than where a callee is. Zig's JS bridge is built entirely
|
|
369
|
+
;; out of this shape —
|
|
370
|
+
;;
|
|
371
|
+
;; pub const namespaceURI = bridge.accessor(Element.getNamespaceUri, null, .{});
|
|
372
|
+
;;
|
|
373
|
+
;; — 2,047 such declarations across 257 files in lightpanda-io/browser, the
|
|
374
|
+
;; project's whole JS↔Zig surface, and NONE of them reached the graph: Zig
|
|
375
|
+
;; emitted no \`value-ref\` capture at all, so a public DOM accessor's only
|
|
376
|
+
;; recorded callers were the two internal ones and \`impact\` called that \`exact\`.
|
|
377
|
+
;;
|
|
378
|
+
;; These become reference-class USES edges through the existing
|
|
379
|
+
;; \`mapReferenceKindToEdgeType\` mapping — a registration is not an invocation
|
|
380
|
+
;; (Kythe \`ref\` vs \`ref/call\`; Joern \`METHOD_REF\`) — resolved by the
|
|
381
|
+
;; property-dispatch pass, which keeps ONLY callable targets. That callable gate
|
|
382
|
+
;; is what makes these deliberately broad rules safe: \`js.Bridge(Element)\` and
|
|
383
|
+
;; \`register(count)\` match too, and emit nothing, exactly as TypeScript's
|
|
384
|
+
;; \`{ port: DEFAULT_PORT }\` does.
|
|
385
|
+
;;
|
|
386
|
+
;; No \`@reference.property-key\` is attached: Zig has no object-literal key to
|
|
387
|
+
;; dispatch through, so these register a reference and never synthesize CALLS.
|
|
388
|
+
;; The terminal invoke — \`Accessor.init\` → a struct field → \`Factory.zig\`'s
|
|
389
|
+
;; \`inline for\`/\`@typeInfo\` → \`@call(.auto, func, args)\` — needs comptime
|
|
390
|
+
;; evaluation and is deliberately NOT modelled; \`impact\` reports the shortfall
|
|
391
|
+
;; as \`epistemic: "lower-bound"\` instead of pretending to certainty.
|
|
392
|
+
|
|
393
|
+
;; Call ARGUMENTS. In tree-sitter-zig arguments are direct children of
|
|
394
|
+
;; \`call_expression\`, NOT wrapped in an \`arguments\` node (only builtins have
|
|
395
|
+
;; one), so the callee has to be consumed explicitly by \`function:\` — without
|
|
396
|
+
;; that binding the same rule also matches the callee of \`foo(bar)\` and mints a
|
|
397
|
+
;; USES edge duplicating the call.
|
|
398
|
+
(call_expression
|
|
399
|
+
function: (_)
|
|
400
|
+
(identifier) @reference.name @reference.value-ref)
|
|
401
|
+
|
|
402
|
+
;; Qualified argument — \`bridge.accessor(Element.getNamespaceUri, …)\`. The
|
|
403
|
+
;; RECEIVER is captured alongside the member so the site carries the owner it
|
|
404
|
+
;; was written with; \`@reference.name\` stays the member, which is the name the
|
|
405
|
+
;; scope walk resolves.
|
|
406
|
+
(call_expression
|
|
407
|
+
function: (_)
|
|
408
|
+
(field_expression
|
|
409
|
+
object: (_) @reference.receiver
|
|
410
|
+
member: (identifier) @reference.name) @reference.value-ref)
|
|
411
|
+
|
|
412
|
+
;; Const binding initialiser — \`pub const defaultHandler = onReset;\`. Both
|
|
413
|
+
;; anchors are load-bearing: the leading \`.\` pins the bound name to the first
|
|
414
|
+
;; named child (see the declaration rules above), and the trailing \`.\` keeps the
|
|
415
|
+
;; initializer as the LAST child, so a \`const x: T = y\` annotation shape cannot
|
|
416
|
+
;; put the TYPE in value position.
|
|
417
|
+
(variable_declaration
|
|
418
|
+
"const" . (identifier)
|
|
419
|
+
(identifier) @reference.name @reference.value-ref .)
|
|
420
|
+
|
|
361
421
|
;; References — free calls: foo(...)
|
|
362
422
|
(call_expression
|
|
363
423
|
function: (identifier) @reference.name) @reference.call.free
|
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Runs after `propagateImportedReturnTypes`, so return bindings hoisted from
|
|
19
19
|
* other files are visible when a subject is a call.
|
|
20
|
+
*
|
|
21
|
+
* This hook also carries `bindZigThisAliases` (`this-alias-bindings.ts`), which
|
|
22
|
+
* is a different job — binding `const Self = @This();` to its container — but
|
|
23
|
+
* needs the same thing this pass is already paying for: the file's parsed tree,
|
|
24
|
+
* post-finalize. Giving it a pass of its own would re-parse every Zig file in
|
|
25
|
+
* the repo whenever the tree cache is cold. That is the whole reason it is
|
|
26
|
+
* here; the two do not otherwise interact, for the reason recorded at the call
|
|
27
|
+
* site.
|
|
20
28
|
*/
|
|
21
29
|
import type { ParsedFile } from '../../../../_shared/index.js';
|
|
22
30
|
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Runs after `propagateImportedReturnTypes`, so return bindings hoisted from
|
|
19
19
|
* other files are visible when a subject is a call.
|
|
20
|
+
*
|
|
21
|
+
* This hook also carries `bindZigThisAliases` (`this-alias-bindings.ts`), which
|
|
22
|
+
* is a different job — binding `const Self = @This();` to its container — but
|
|
23
|
+
* needs the same thing this pass is already paying for: the file's parsed tree,
|
|
24
|
+
* post-finalize. Giving it a pass of its own would re-parse every Zig file in
|
|
25
|
+
* the repo whenever the tree cache is cold. That is the whole reason it is
|
|
26
|
+
* here; the two do not otherwise interact, for the reason recorded at the call
|
|
27
|
+
* site.
|
|
20
28
|
*/
|
|
21
29
|
import { getZigParser } from './query.js';
|
|
22
30
|
import { getTreeSitterBufferSize } from '../../constants.js';
|
|
@@ -24,6 +32,7 @@ import { parseSourceSafe, ParseTimeoutError } from '../../../tree-sitter/safe-pa
|
|
|
24
32
|
import { logger } from '../../../logger.js';
|
|
25
33
|
import { findClassBindingInScope, findReceiverTypeBinding, isClassLike, } from '../../scope-resolution/scope/walkers.js';
|
|
26
34
|
import { isZigKeywordDeclaration, zigUnwrapValue } from './captures.js';
|
|
35
|
+
import { bindZigThisAliases } from './this-alias-bindings.js';
|
|
27
36
|
import { normalizeZigTypeName } from './interpret.js';
|
|
28
37
|
const PAYLOAD_HOSTS = new Set([
|
|
29
38
|
'for_statement',
|
|
@@ -70,6 +79,21 @@ export function populateZigRangeBindings(parsedFiles, indexes, ctx) {
|
|
|
70
79
|
const scopes = parsed.scopes;
|
|
71
80
|
if (scopes.length === 0)
|
|
72
81
|
continue;
|
|
82
|
+
// `const Self = @This();` — bind the alias to its container. It sits in this
|
|
83
|
+
// loop for ONE reason: the tree. A pass of its own would re-parse every Zig
|
|
84
|
+
// file in the repo whenever the tree cache is cold.
|
|
85
|
+
//
|
|
86
|
+
// Its position relative to the payload walk below is NOT load-bearing, and
|
|
87
|
+
// saying otherwise would be wrong in a checkable way: the payload walk types
|
|
88
|
+
// a subject through `findReceiverTypeBinding`, which reads `typeBindings`
|
|
89
|
+
// and the namespace/workspace type channels — never `bindingAugmentations`,
|
|
90
|
+
// where this writes. Measured on `for (Self.items) |it|`: `it` is bound
|
|
91
|
+
// neither before nor after. Nor is that a gap this should close by also
|
|
92
|
+
// writing a typeBinding — NO container name has one, the file stem included,
|
|
93
|
+
// so a payload subject written `Type.member` resolves for no spelling at
|
|
94
|
+
// all, and giving the alias an entry would make it behave unlike the very
|
|
95
|
+
// container it names.
|
|
96
|
+
bindZigThisAliases(parsed, tree.rootNode, indexes);
|
|
73
97
|
const resolver = new ZigSubjectTypeResolver(scopes, indexes, classScopeByDefId);
|
|
74
98
|
// Pre-order: an outer payload is bound before an inner construct reads it
|
|
75
99
|
// (`for (pages) |p| { if (p.frame) |f| … }`).
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Thin wiring: Zig has no inheritance (default MRO linearization over an
|
|
6
6
|
* empty heritage set), no `super`, and is statically typed (field-fallback
|
|
7
7
|
* heuristic off per the contract guidance). Import resolution reuses the
|
|
8
|
-
* same `resolveZigImportInternal` the legacy import-resolver config wraps,
|
|
9
|
-
*
|
|
8
|
+
* same `resolveZigImportInternal` the legacy import-resolver config wraps, with
|
|
9
|
+
* the repo's Zig build packages threaded through `loadResolutionConfig` and the
|
|
10
|
+
* one governing each file selected per-file by `zigPackageFor`.
|
|
10
11
|
*/
|
|
11
12
|
import type { ScopeResolver } from '../../scope-resolution/contract/scope-resolver.js';
|
|
12
13
|
export declare const zigScopeResolver: ScopeResolver;
|