gitnexus 1.6.5-rc.41 → 1.6.5-rc.42
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/core/ingestion/languages/cpp/adl.d.ts +3 -2
- package/dist/core/ingestion/languages/cpp/adl.js +31 -10
- package/dist/core/ingestion/languages/cpp/captures.js +21 -8
- package/dist/core/ingestion/languages/cpp/file-local-linkage.d.ts +25 -0
- package/dist/core/ingestion/languages/cpp/file-local-linkage.js +72 -1
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +5 -4
- package/package.json +1 -1
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
* - `audit::Event& r`, `audit::Event&& rr`
|
|
22
22
|
* - `std::vector<audit::Event>` (template namespace + template-arg namespaces)
|
|
23
23
|
*
|
|
24
|
-
* Function-pointer arguments
|
|
25
|
-
*
|
|
24
|
+
* Function-pointer arguments and the rest of the full closure are still
|
|
25
|
+
* deliberately excluded. V2 additionally walks class ancestors (via MRO),
|
|
26
|
+
* so base-class enclosing namespaces also contribute associated namespaces.
|
|
26
27
|
*
|
|
27
28
|
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
28
29
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
* - `audit::Event& r`, `audit::Event&& rr`
|
|
22
22
|
* - `std::vector<audit::Event>` (template namespace + template-arg namespaces)
|
|
23
23
|
*
|
|
24
|
-
* Function-pointer arguments
|
|
25
|
-
*
|
|
24
|
+
* Function-pointer arguments and the rest of the full closure are still
|
|
25
|
+
* deliberately excluded. V2 additionally walks class ancestors (via MRO),
|
|
26
|
+
* so base-class enclosing namespaces also contribute associated namespaces.
|
|
26
27
|
*
|
|
27
28
|
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
28
29
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
@@ -210,12 +211,23 @@ function collectAssociatedNamespacesForAdlArg(arg, scopes, associatedNamespaces)
|
|
|
210
211
|
function addAssociatedNamespaceForClassName(simpleClassName, scopes, associatedNamespaces) {
|
|
211
212
|
if (simpleClassName.length === 0)
|
|
212
213
|
return;
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
214
|
+
const classLookup = findCppClassDefBySimpleName(simpleClassName, scopes);
|
|
215
|
+
if (classLookup === undefined)
|
|
215
216
|
return;
|
|
217
|
+
const { classDef, ambiguous } = classLookup;
|
|
216
218
|
const nsQName = classToNamespaceQualifiedName.get(classDef.nodeId);
|
|
217
219
|
if (nsQName !== undefined)
|
|
218
220
|
associatedNamespaces.add(nsQName);
|
|
221
|
+
// Preserve V1 collision behavior for the direct class namespace, but avoid
|
|
222
|
+
// amplifying a same-simple-name collision by walking an arbitrary class's
|
|
223
|
+
// full MRO chain.
|
|
224
|
+
if (ambiguous)
|
|
225
|
+
return;
|
|
226
|
+
for (const ancestorDefId of scopes.methodDispatch.mroFor(classDef.nodeId)) {
|
|
227
|
+
const ancestorNsQName = classToNamespaceQualifiedName.get(ancestorDefId);
|
|
228
|
+
if (ancestorNsQName !== undefined)
|
|
229
|
+
associatedNamespaces.add(ancestorNsQName);
|
|
230
|
+
}
|
|
219
231
|
}
|
|
220
232
|
/** Walk upward from a Class scope, finding the innermost enclosing
|
|
221
233
|
* Namespace scope, and return that namespace's qualified name (dot-
|
|
@@ -285,16 +297,25 @@ function findNamespaceDefInScope(scope) {
|
|
|
285
297
|
return undefined;
|
|
286
298
|
}
|
|
287
299
|
/** Find a class-like def by simple name across the workspace. V1
|
|
288
|
-
* arbitrary-
|
|
289
|
-
*
|
|
290
|
-
*
|
|
300
|
+
* still arbitrary-picks the first class on collisions (multiple classes
|
|
301
|
+
* share the simple name), but reports the collision so callers can avoid
|
|
302
|
+
* amplifying that uncertainty (for example by skipping MRO expansion).
|
|
303
|
+
* C++ ADL strictness would require full type-driven lookup. */
|
|
291
304
|
function findCppClassDefBySimpleName(simpleName, scopes) {
|
|
305
|
+
let firstMatch;
|
|
292
306
|
for (const def of scopes.defs.byId.values()) {
|
|
293
307
|
if (def.type !== 'Class' && def.type !== 'Struct' && def.type !== 'Interface')
|
|
294
308
|
continue;
|
|
295
309
|
const simple = def.qualifiedName?.split('.').pop() ?? def.qualifiedName ?? '';
|
|
296
|
-
if (simple
|
|
297
|
-
|
|
310
|
+
if (simple !== simpleName)
|
|
311
|
+
continue;
|
|
312
|
+
if (firstMatch === undefined) {
|
|
313
|
+
firstMatch = def;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
return { classDef: firstMatch, ambiguous: true };
|
|
298
317
|
}
|
|
299
|
-
|
|
318
|
+
if (firstMatch === undefined)
|
|
319
|
+
return undefined;
|
|
320
|
+
return { classDef: firstMatch, ambiguous: false };
|
|
300
321
|
}
|
|
@@ -4,7 +4,7 @@ import { getTreeSitterBufferSize } from '../../constants.js';
|
|
|
4
4
|
import { parseSourceSafe } from '../../../tree-sitter/safe-parse.js';
|
|
5
5
|
import { splitCppInclude, splitCppUsingDecl } from './import-decomposer.js';
|
|
6
6
|
import { computeCppDeclarationArity, computeCppCallArity } from './arity-metadata.js';
|
|
7
|
-
import { markFileLocal } from './file-local-linkage.js';
|
|
7
|
+
import { markCppAnonymousNamespaceRange, markFileLocal } from './file-local-linkage.js';
|
|
8
8
|
import { markCppDependentBase } from './two-phase-lookup.js';
|
|
9
9
|
import { markCppAdlSiteArgs, markCppAdlSiteNoAdl } from './adl.js';
|
|
10
10
|
import { markCppInlineNamespaceRange } from './inline-namespaces.js';
|
|
@@ -150,20 +150,33 @@ export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
150
150
|
// namespace's source range so `populateCppInlineNamespaceScopes`
|
|
151
151
|
// (during populateOwners) can match it back to the corresponding
|
|
152
152
|
// Namespace scope.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
// `@declaration.namespace` fires only for NAMED namespaces (the query
|
|
154
|
+
// requires a `name: (namespace_identifier)` child). Use the unconditional
|
|
155
|
+
// `@scope.namespace` capture so the anonymous-namespace branch also runs.
|
|
156
|
+
const namespaceScopeAnchor = grouped['@declaration.namespace'] ?? grouped['@scope.namespace'];
|
|
157
|
+
if (namespaceScopeAnchor !== undefined) {
|
|
158
|
+
const nsNode = findNodeAtRange(tree.rootNode, namespaceScopeAnchor.range, 'namespace_definition');
|
|
159
|
+
if (nsNode !== null) {
|
|
157
160
|
// Range coords stored in the shared Range shape use 1-based
|
|
158
161
|
// line numbers (see `ast-helpers.ts` rangeForNode where
|
|
159
162
|
// `startPosition.row + 1` is applied). Match that convention so
|
|
160
|
-
//
|
|
161
|
-
|
|
163
|
+
// the populators can join against `Scope.range`.
|
|
164
|
+
const nsRange = {
|
|
162
165
|
startLine: nsNode.startPosition.row + 1,
|
|
163
166
|
startCol: nsNode.startPosition.column,
|
|
164
167
|
endLine: nsNode.endPosition.row + 1,
|
|
165
168
|
endCol: nsNode.endPosition.column,
|
|
166
|
-
}
|
|
169
|
+
};
|
|
170
|
+
if (isInlineNamespace(nsNode)) {
|
|
171
|
+
markCppInlineNamespaceRange(filePath, nsRange);
|
|
172
|
+
}
|
|
173
|
+
// Anonymous namespace: `namespace_definition` with no `name` field.
|
|
174
|
+
// Recorded so `expandCppWildcardNames` can propagate its members
|
|
175
|
+
// to including TUs even though their names are also `markFileLocal`'d
|
|
176
|
+
// (which blocks the global free-call fallback's cross-file path).
|
|
177
|
+
if ((nsNode.childForFieldName?.('name') ?? null) === null) {
|
|
178
|
+
markCppAnonymousNamespaceRange(filePath, nsRange);
|
|
179
|
+
}
|
|
167
180
|
}
|
|
168
181
|
}
|
|
169
182
|
// ── ADL (Koenig lookup) per-site recording ──────────────────────
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import type { ParsedFile, ScopeId } from '../../../../_shared/index.js';
|
|
2
|
+
interface RangeKeyShape {
|
|
3
|
+
readonly startLine: number;
|
|
4
|
+
readonly startCol: number;
|
|
5
|
+
readonly endLine: number;
|
|
6
|
+
readonly endCol: number;
|
|
7
|
+
}
|
|
2
8
|
/** Record a symbol name as file-local (static or anonymous namespace). */
|
|
3
9
|
export declare function markFileLocal(filePath: string, name: string): void;
|
|
4
10
|
/** Check whether a symbol name has file-local linkage in the given file. */
|
|
5
11
|
export declare function isFileLocal(filePath: string, name: string): boolean;
|
|
12
|
+
/** Capture-time: record an anonymous `namespace_definition` source range. */
|
|
13
|
+
export declare function markCppAnonymousNamespaceRange(filePath: string, range: RangeKeyShape): void;
|
|
14
|
+
/** Predicate consumed by `populateCppNonGloballyVisible` and
|
|
15
|
+
* `expandCppWildcardNames` to exempt anonymous-namespace scopes from
|
|
16
|
+
* the cross-file unqualified-lookup exclusion that applies to ordinary
|
|
17
|
+
* named namespaces. */
|
|
18
|
+
export declare function isCppAnonymousNamespaceScope(scopeId: ScopeId): boolean;
|
|
6
19
|
/** Clear tracked file-local names (call at start of each resolution pass). */
|
|
7
20
|
export declare function clearFileLocalNames(): void;
|
|
21
|
+
/** Resolve recorded anonymous-namespace source ranges to `ScopeId`s.
|
|
22
|
+
* Must run inside `populateOwners` BEFORE `populateCppNonGloballyVisible`
|
|
23
|
+
* consults the resolved set. */
|
|
24
|
+
export declare function populateCppAnonymousNamespaceScopes(parsed: {
|
|
25
|
+
readonly filePath: string;
|
|
26
|
+
readonly scopes: readonly {
|
|
27
|
+
readonly id: ScopeId;
|
|
28
|
+
readonly kind: string;
|
|
29
|
+
readonly range: RangeKeyShape;
|
|
30
|
+
}[];
|
|
31
|
+
}): void;
|
|
8
32
|
/**
|
|
9
33
|
* Populate per-file "not globally visible" nodeIds by walking the parsed
|
|
10
34
|
* file's scopes. Run as part of the `populateOwners` hook so every C++
|
|
@@ -91,3 +115,4 @@ export declare function isCppDefGloballyVisible(filePath: string, nodeId: string
|
|
|
91
115
|
* members never enter `localDefs` unqualified.
|
|
92
116
|
*/
|
|
93
117
|
export declare function expandCppWildcardNames(targetModuleScope: ScopeId, parsedFiles: readonly ParsedFile[]): readonly string[];
|
|
118
|
+
export {};
|
|
@@ -31,6 +31,24 @@ const fileLocalNames = new Map();
|
|
|
31
31
|
* this set encodes).
|
|
32
32
|
*/
|
|
33
33
|
const nonGloballyVisibleNodeIds = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* Per-file set of source-range keys identifying `namespace { ... }` blocks.
|
|
36
|
+
* Resolved to `ScopeId`s in `populateCppAnonymousNamespaceScopes` and
|
|
37
|
+
* consumed via `isCppAnonymousNamespaceScope`.
|
|
38
|
+
*
|
|
39
|
+
* Anonymous namespaces have file-local linkage but, unlike `static`, their
|
|
40
|
+
* members propagate to any TU that `#include`s the declaring file — each
|
|
41
|
+
* including TU gets its own internal-linkage copy. So for wildcard import
|
|
42
|
+
* expansion (`expandCppWildcardNames`) we treat anonymous-namespace owned
|
|
43
|
+
* defs as if declared at the enclosing scope. Cross-file unqualified
|
|
44
|
+
* lookup that does NOT go through `#include` is still blocked by the
|
|
45
|
+
* `isFileLocal` mark recorded on the def's name.
|
|
46
|
+
*/
|
|
47
|
+
const anonymousNamespaceRangesByFile = new Map();
|
|
48
|
+
const anonymousNamespaceScopeIds = new Set();
|
|
49
|
+
function rangeKey(r) {
|
|
50
|
+
return `${r.startLine}:${r.startCol}:${r.endLine}:${r.endCol}`;
|
|
51
|
+
}
|
|
34
52
|
/** Record a symbol name as file-local (static or anonymous namespace). */
|
|
35
53
|
export function markFileLocal(filePath, name) {
|
|
36
54
|
let names = fileLocalNames.get(filePath);
|
|
@@ -44,10 +62,43 @@ export function markFileLocal(filePath, name) {
|
|
|
44
62
|
export function isFileLocal(filePath, name) {
|
|
45
63
|
return fileLocalNames.get(filePath)?.has(name) ?? false;
|
|
46
64
|
}
|
|
65
|
+
/** Capture-time: record an anonymous `namespace_definition` source range. */
|
|
66
|
+
export function markCppAnonymousNamespaceRange(filePath, range) {
|
|
67
|
+
let set = anonymousNamespaceRangesByFile.get(filePath);
|
|
68
|
+
if (set === undefined) {
|
|
69
|
+
set = new Set();
|
|
70
|
+
anonymousNamespaceRangesByFile.set(filePath, set);
|
|
71
|
+
}
|
|
72
|
+
set.add(rangeKey(range));
|
|
73
|
+
}
|
|
74
|
+
/** Predicate consumed by `populateCppNonGloballyVisible` and
|
|
75
|
+
* `expandCppWildcardNames` to exempt anonymous-namespace scopes from
|
|
76
|
+
* the cross-file unqualified-lookup exclusion that applies to ordinary
|
|
77
|
+
* named namespaces. */
|
|
78
|
+
export function isCppAnonymousNamespaceScope(scopeId) {
|
|
79
|
+
return anonymousNamespaceScopeIds.has(scopeId);
|
|
80
|
+
}
|
|
47
81
|
/** Clear tracked file-local names (call at start of each resolution pass). */
|
|
48
82
|
export function clearFileLocalNames() {
|
|
49
83
|
fileLocalNames.clear();
|
|
50
84
|
nonGloballyVisibleNodeIds.clear();
|
|
85
|
+
anonymousNamespaceRangesByFile.clear();
|
|
86
|
+
anonymousNamespaceScopeIds.clear();
|
|
87
|
+
}
|
|
88
|
+
/** Resolve recorded anonymous-namespace source ranges to `ScopeId`s.
|
|
89
|
+
* Must run inside `populateOwners` BEFORE `populateCppNonGloballyVisible`
|
|
90
|
+
* consults the resolved set. */
|
|
91
|
+
export function populateCppAnonymousNamespaceScopes(parsed) {
|
|
92
|
+
const ranges = anonymousNamespaceRangesByFile.get(parsed.filePath);
|
|
93
|
+
if (ranges === undefined || ranges.size === 0)
|
|
94
|
+
return;
|
|
95
|
+
for (const scope of parsed.scopes) {
|
|
96
|
+
if (scope.kind !== 'Namespace')
|
|
97
|
+
continue;
|
|
98
|
+
if (ranges.has(rangeKey(scope.range))) {
|
|
99
|
+
anonymousNamespaceScopeIds.add(scope.id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
51
102
|
}
|
|
52
103
|
/**
|
|
53
104
|
* Populate per-file "not globally visible" nodeIds by walking the parsed
|
|
@@ -75,6 +126,14 @@ export function populateCppNonGloballyVisible(parsed) {
|
|
|
75
126
|
// unqualified lookup can still see their callable defs.
|
|
76
127
|
if (scope.kind === 'Namespace' && isCppInlineNamespaceScope(scope.id))
|
|
77
128
|
continue;
|
|
129
|
+
// Anonymous namespaces give internal linkage but their contents are
|
|
130
|
+
// visible at the enclosing scope within the same TU and propagate to
|
|
131
|
+
// any TU that `#include`s the declaring file. The `isFileLocal` mark
|
|
132
|
+
// (recorded on the def's name in this file) still blocks cross-file
|
|
133
|
+
// unqualified lookup that does not go through #include, so dropping
|
|
134
|
+
// the structural visibility exclusion here is safe.
|
|
135
|
+
if (scope.kind === 'Namespace' && anonymousNamespaceScopeIds.has(scope.id))
|
|
136
|
+
continue;
|
|
78
137
|
for (const def of scope.ownedDefs) {
|
|
79
138
|
set.add(def.nodeId);
|
|
80
139
|
}
|
|
@@ -173,15 +232,27 @@ export function expandCppWildcardNames(targetModuleScope, parsedFiles) {
|
|
|
173
232
|
// including TU. When the owning scope is unknown we default to
|
|
174
233
|
// include (preserves prior behavior for any def whose structural
|
|
175
234
|
// ownership wasn't recorded in `Scope.ownedDefs`).
|
|
235
|
+
//
|
|
236
|
+
// Anonymous namespaces are exempt: their members propagate to the
|
|
237
|
+
// enclosing scope of any TU that #includes the declaring file (each
|
|
238
|
+
// including TU gets its own internal-linkage copy per ISO C++).
|
|
176
239
|
const ownerScope = ownerScopeByNodeId.get(def.nodeId);
|
|
240
|
+
const ownerIsAnonymousNamespace = ownerScope !== undefined &&
|
|
241
|
+
ownerScope.kind === 'Namespace' &&
|
|
242
|
+
anonymousNamespaceScopeIds.has(ownerScope.id);
|
|
177
243
|
if (ownerScope !== undefined &&
|
|
244
|
+
!ownerIsAnonymousNamespace &&
|
|
178
245
|
(ownerScope.kind === 'Namespace' || ownerScope.kind === 'Class')) {
|
|
179
246
|
continue;
|
|
180
247
|
}
|
|
181
248
|
const name = simpleName(def);
|
|
182
249
|
if (name === '')
|
|
183
250
|
continue;
|
|
184
|
-
|
|
251
|
+
// Same exemption for the `isFileLocal` mark — anonymous-namespace
|
|
252
|
+
// names are recorded as file-local to suppress the global free-call
|
|
253
|
+
// fallback's cross-file leak, but they MUST still propagate through
|
|
254
|
+
// wildcard import expansion to including TUs.
|
|
255
|
+
if (!ownerIsAnonymousNamespace && isFileLocal(target.filePath, name))
|
|
185
256
|
continue;
|
|
186
257
|
if (seen.has(name))
|
|
187
258
|
continue;
|
|
@@ -7,7 +7,7 @@ import { cppArityCompatibility } from './arity.js';
|
|
|
7
7
|
import { cppMergeBindings } from './merge-bindings.js';
|
|
8
8
|
import { resolveCppImportTarget } from './import-target.js';
|
|
9
9
|
import { scanCppHeaderFiles } from './header-scan.js';
|
|
10
|
-
import { expandCppWildcardNames, isFileLocal, clearFileLocalNames, populateCppNonGloballyVisible, isCppDefGloballyVisible, } from './file-local-linkage.js';
|
|
10
|
+
import { expandCppWildcardNames, isFileLocal, clearFileLocalNames, populateCppAnonymousNamespaceScopes, populateCppNonGloballyVisible, isCppDefGloballyVisible, } from './file-local-linkage.js';
|
|
11
11
|
import { populateCppDependentBases, clearCppDependentBases, isCppDependentBaseMember, } from './two-phase-lookup.js';
|
|
12
12
|
import { populateCppAssociatedNamespaces, clearCppAdlState, pickCppAdlCandidates, ADL_AMBIGUOUS, } from './adl.js';
|
|
13
13
|
import { clearCppInlineNamespaces, populateCppInlineNamespaceScopes, resolveCppQualifiedNamespaceMember, } from './inline-namespaces.js';
|
|
@@ -61,10 +61,11 @@ export const cppScopeResolver = {
|
|
|
61
61
|
buildMro: (graph, parsedFiles, nodeLookup) => buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
|
|
62
62
|
populateOwners: (parsed) => {
|
|
63
63
|
populateClassOwnedMembers(parsed);
|
|
64
|
-
// Resolve inline-namespace ranges (recorded at capture
|
|
65
|
-
// ScopeIds BEFORE `populateCppNonGloballyVisible` runs, so
|
|
66
|
-
//
|
|
64
|
+
// Resolve inline- and anonymous-namespace ranges (recorded at capture
|
|
65
|
+
// time) to ScopeIds BEFORE `populateCppNonGloballyVisible` runs, so
|
|
66
|
+
// both exemptions see the populated Sets.
|
|
67
67
|
populateCppInlineNamespaceScopes(parsed);
|
|
68
|
+
populateCppAnonymousNamespaceScopes(parsed);
|
|
68
69
|
// Track namespace-nested and class-nested defs so the global free-call
|
|
69
70
|
// fallback and wildcard expansion can suppress them as unqualified
|
|
70
71
|
// cross-file callables.
|
package/package.json
CHANGED