gitnexus 1.6.5-rc.40 → 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 +22 -9
- package/dist/core/ingestion/languages/cpp/adl.js +64 -22
- package/dist/core/ingestion/languages/cpp/captures.js +172 -17
- 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 +9 -6
- package/package.json +1 -1
|
@@ -15,14 +15,15 @@
|
|
|
15
15
|
*
|
|
16
16
|
* ## Current boundary
|
|
17
17
|
*
|
|
18
|
-
* The current implementation covers
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
18
|
+
* The current implementation covers class-typed arguments (value, pointer,
|
|
19
|
+
* and reference) and template specializations with explicit type arguments:
|
|
20
|
+
* - `audit::Event e`, `audit::Event* p`, `audit::Event** pp`
|
|
21
|
+
* - `audit::Event& r`, `audit::Event&& rr`
|
|
22
|
+
* - `std::vector<audit::Event>` (template namespace + template-arg namespaces)
|
|
23
|
+
*
|
|
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
|
|
@@ -61,8 +62,20 @@ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexe
|
|
|
61
62
|
*/
|
|
62
63
|
export interface CppAdlArgInfo {
|
|
63
64
|
/** Simple class-like type name (last segment of qualified name); empty
|
|
64
|
-
* for primitives, literals, function pointers,
|
|
65
|
+
* for primitives, literals, function pointers, etc. */
|
|
65
66
|
readonly simpleClassName: string;
|
|
67
|
+
/** Template's own simple class-like name (e.g. `vector` for
|
|
68
|
+
* `std::vector<N::T>`), empty when arg type is not a template spec. */
|
|
69
|
+
readonly templateSimpleClassName: string;
|
|
70
|
+
/** Template's own enclosing namespace (dot-qualified, e.g. `std`), empty
|
|
71
|
+
* when unavailable / unqualified. */
|
|
72
|
+
readonly templateNamespace: string;
|
|
73
|
+
/** Class-like names extracted from explicit type template arguments,
|
|
74
|
+
* recursively bounded. */
|
|
75
|
+
readonly templateArgClassNames: readonly string[];
|
|
76
|
+
/** Enclosing namespaces extracted from explicit type template arguments,
|
|
77
|
+
* recursively bounded. */
|
|
78
|
+
readonly templateArgNamespaces: readonly string[];
|
|
66
79
|
}
|
|
67
80
|
/** Sentinel returned by `pickCppAdlCandidates` when ADL surfaces multiple
|
|
68
81
|
* candidates that share normalized parameter types — the caller MUST
|
|
@@ -15,14 +15,15 @@
|
|
|
15
15
|
*
|
|
16
16
|
* ## Current boundary
|
|
17
17
|
*
|
|
18
|
-
* The current implementation covers
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
18
|
+
* The current implementation covers class-typed arguments (value, pointer,
|
|
19
|
+
* and reference) and template specializations with explicit type arguments:
|
|
20
|
+
* - `audit::Event e`, `audit::Event* p`, `audit::Event** pp`
|
|
21
|
+
* - `audit::Event& r`, `audit::Event&& rr`
|
|
22
|
+
* - `std::vector<audit::Event>` (template namespace + template-arg namespaces)
|
|
23
|
+
*
|
|
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
|
|
@@ -132,14 +133,7 @@ export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
|
132
133
|
// Collect associated namespace QNames from every participating class-typed arg.
|
|
133
134
|
const associatedNamespaces = new Set();
|
|
134
135
|
for (const arg of args) {
|
|
135
|
-
|
|
136
|
-
continue;
|
|
137
|
-
const classDef = findCppClassDefBySimpleName(arg.simpleClassName, scopes);
|
|
138
|
-
if (classDef === undefined)
|
|
139
|
-
continue;
|
|
140
|
-
const nsQName = classToNamespaceQualifiedName.get(classDef.nodeId);
|
|
141
|
-
if (nsQName !== undefined)
|
|
142
|
-
associatedNamespaces.add(nsQName);
|
|
136
|
+
collectAssociatedNamespacesForAdlArg(arg, scopes, associatedNamespaces);
|
|
143
137
|
}
|
|
144
138
|
if (associatedNamespaces.size === 0)
|
|
145
139
|
return undefined;
|
|
@@ -196,6 +190,45 @@ export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
|
196
190
|
// unique-survivor requirement (see `pick-implicit-this-overload.test.ts`).
|
|
197
191
|
return ADL_AMBIGUOUS;
|
|
198
192
|
}
|
|
193
|
+
function collectAssociatedNamespacesForAdlArg(arg, scopes, associatedNamespaces) {
|
|
194
|
+
// For template args this may be the template name itself (e.g. `vector`);
|
|
195
|
+
// simple-name lookup can match project classes with the same name (known
|
|
196
|
+
// V1/V2 simplification).
|
|
197
|
+
addAssociatedNamespaceForClassName(arg.simpleClassName, scopes, associatedNamespaces);
|
|
198
|
+
// Includes template-owner namespaces (e.g. `std` in std::vector<T>). If
|
|
199
|
+
// that surfaces extra candidates, ADL_AMBIGUOUS suppression below prevents
|
|
200
|
+
// arbitrary edge emission.
|
|
201
|
+
if (arg.templateNamespace.length > 0)
|
|
202
|
+
associatedNamespaces.add(arg.templateNamespace);
|
|
203
|
+
for (const ns of arg.templateArgNamespaces) {
|
|
204
|
+
if (ns.length > 0)
|
|
205
|
+
associatedNamespaces.add(ns);
|
|
206
|
+
}
|
|
207
|
+
for (const className of arg.templateArgClassNames) {
|
|
208
|
+
addAssociatedNamespaceForClassName(className, scopes, associatedNamespaces);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function addAssociatedNamespaceForClassName(simpleClassName, scopes, associatedNamespaces) {
|
|
212
|
+
if (simpleClassName.length === 0)
|
|
213
|
+
return;
|
|
214
|
+
const classLookup = findCppClassDefBySimpleName(simpleClassName, scopes);
|
|
215
|
+
if (classLookup === undefined)
|
|
216
|
+
return;
|
|
217
|
+
const { classDef, ambiguous } = classLookup;
|
|
218
|
+
const nsQName = classToNamespaceQualifiedName.get(classDef.nodeId);
|
|
219
|
+
if (nsQName !== undefined)
|
|
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
|
+
}
|
|
231
|
+
}
|
|
199
232
|
/** Walk upward from a Class scope, finding the innermost enclosing
|
|
200
233
|
* Namespace scope, and return that namespace's qualified name (dot-
|
|
201
234
|
* joined, outermost-first). Returns '' when the class has no enclosing
|
|
@@ -264,16 +297,25 @@ function findNamespaceDefInScope(scope) {
|
|
|
264
297
|
return undefined;
|
|
265
298
|
}
|
|
266
299
|
/** Find a class-like def by simple name across the workspace. V1
|
|
267
|
-
* arbitrary-
|
|
268
|
-
*
|
|
269
|
-
*
|
|
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. */
|
|
270
304
|
function findCppClassDefBySimpleName(simpleName, scopes) {
|
|
305
|
+
let firstMatch;
|
|
271
306
|
for (const def of scopes.defs.byId.values()) {
|
|
272
307
|
if (def.type !== 'Class' && def.type !== 'Struct' && def.type !== 'Interface')
|
|
273
308
|
continue;
|
|
274
309
|
const simple = def.qualifiedName?.split('.').pop() ?? def.qualifiedName ?? '';
|
|
275
|
-
if (simple
|
|
276
|
-
|
|
310
|
+
if (simple !== simpleName)
|
|
311
|
+
continue;
|
|
312
|
+
if (firstMatch === undefined) {
|
|
313
|
+
firstMatch = def;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
return { classDef: firstMatch, ambiguous: true };
|
|
277
317
|
}
|
|
278
|
-
|
|
318
|
+
if (firstMatch === undefined)
|
|
319
|
+
return undefined;
|
|
320
|
+
return { classDef: firstMatch, ambiguous: false };
|
|
279
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 ──────────────────────
|
|
@@ -657,12 +670,14 @@ function isParenthesizedFunctionCall(callNode) {
|
|
|
657
670
|
}
|
|
658
671
|
/**
|
|
659
672
|
* Per-argument ADL classification: walk each argument of a free call and
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
* pointer
|
|
673
|
+
* classify its declared type for associated-namespace lookup.
|
|
674
|
+
*
|
|
675
|
+
* Value/pointer/reference class-typed args and template specializations
|
|
676
|
+
* with explicit type arguments contribute; function pointers, primitives,
|
|
677
|
+
* literals, and other unsupported shapes produce an empty result.
|
|
663
678
|
*
|
|
664
|
-
* Class-typed values
|
|
665
|
-
* preserve the
|
|
679
|
+
* Class-typed values/pointers/references (`N::S`, `N::S*`, `N::S&`) all
|
|
680
|
+
* preserve the class name for associated-namespace lookup.
|
|
666
681
|
* Function pointers remain excluded even when their return type names a
|
|
667
682
|
* class, because the associated entity is the pointed-to function type,
|
|
668
683
|
* not the return type.
|
|
@@ -682,7 +697,14 @@ function inferCppCallAdlArgs(callNode) {
|
|
|
682
697
|
}
|
|
683
698
|
return out;
|
|
684
699
|
}
|
|
685
|
-
const
|
|
700
|
+
const ADL_TEMPLATE_RECURSION_MAX_DEPTH = 8;
|
|
701
|
+
const EMPTY_ADL_ARG = {
|
|
702
|
+
simpleClassName: '',
|
|
703
|
+
templateSimpleClassName: '',
|
|
704
|
+
templateNamespace: '',
|
|
705
|
+
templateArgClassNames: [],
|
|
706
|
+
templateArgNamespaces: [],
|
|
707
|
+
};
|
|
686
708
|
function classifyAdlArg(argNode) {
|
|
687
709
|
// Literals and primitive-shaped expressions never have associated namespaces.
|
|
688
710
|
if (argNode.type === 'number_literal' ||
|
|
@@ -783,21 +805,51 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
783
805
|
if (isFunctionPointer || nameText !== varName)
|
|
784
806
|
continue;
|
|
785
807
|
const simpleClassName = extractAdlSimpleTypeName(typeNode);
|
|
786
|
-
|
|
808
|
+
const { templateSimpleClassName, templateNamespace, templateArgClassNames, templateArgNamespaces, } = extractAdlTemplateInfo(typeNode);
|
|
809
|
+
return {
|
|
810
|
+
simpleClassName,
|
|
811
|
+
templateSimpleClassName,
|
|
812
|
+
templateNamespace,
|
|
813
|
+
templateArgClassNames,
|
|
814
|
+
templateArgNamespaces,
|
|
815
|
+
};
|
|
787
816
|
}
|
|
788
817
|
return EMPTY_ADL_ARG;
|
|
789
818
|
}
|
|
790
819
|
/** Extract the simple class-like type name from a `type:` field node.
|
|
791
|
-
* Returns '' for primitives
|
|
820
|
+
* Returns '' for primitives and any other
|
|
792
821
|
* unsupported type-only shape. Function pointers are filtered at the
|
|
793
822
|
* declarator level in `lookupAdlIdentifierType`. */
|
|
794
823
|
function extractAdlSimpleTypeName(typeNode) {
|
|
824
|
+
if (typeNode.type === 'type_descriptor') {
|
|
825
|
+
const innerType = typeNode.childForFieldName('type');
|
|
826
|
+
if (innerType !== null)
|
|
827
|
+
return extractAdlSimpleTypeName(innerType);
|
|
828
|
+
for (let i = 0; i < typeNode.childCount; i++) {
|
|
829
|
+
const child = typeNode.child(i);
|
|
830
|
+
if (child === null)
|
|
831
|
+
continue;
|
|
832
|
+
if (child.type === 'type_identifier' ||
|
|
833
|
+
child.type === 'qualified_identifier' ||
|
|
834
|
+
child.type === 'template_type') {
|
|
835
|
+
return extractAdlSimpleTypeName(child);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return '';
|
|
839
|
+
}
|
|
795
840
|
if (typeNode.type === 'primitive_type')
|
|
796
841
|
return '';
|
|
797
842
|
if (typeNode.type === 'sized_type_specifier')
|
|
798
843
|
return '';
|
|
799
844
|
if (typeNode.type === 'type_identifier')
|
|
800
845
|
return typeNode.text;
|
|
846
|
+
if (typeNode.type === 'template_type') {
|
|
847
|
+
const nameNode = typeNode.childForFieldName('name');
|
|
848
|
+
if (nameNode !== null)
|
|
849
|
+
return extractAdlSimpleTypeName(nameNode);
|
|
850
|
+
const id = findFirstDescendantOfType(typeNode, 'type_identifier');
|
|
851
|
+
return id !== null ? id.text : '';
|
|
852
|
+
}
|
|
801
853
|
if (typeNode.type === 'qualified_identifier') {
|
|
802
854
|
const nameNode = typeNode.childForFieldName('name');
|
|
803
855
|
if (nameNode !== null)
|
|
@@ -805,9 +857,112 @@ function extractAdlSimpleTypeName(typeNode) {
|
|
|
805
857
|
const id = findFirstDescendantOfType(typeNode, 'type_identifier');
|
|
806
858
|
return id !== null ? id.text : '';
|
|
807
859
|
}
|
|
808
|
-
//
|
|
860
|
+
// Function pointers, decltype, etc — unsupported for ADL participation.
|
|
861
|
+
return '';
|
|
862
|
+
}
|
|
863
|
+
function extractAdlTypeNamespace(typeNode) {
|
|
864
|
+
if (typeNode.type === 'type_descriptor') {
|
|
865
|
+
const innerType = typeNode.childForFieldName('type');
|
|
866
|
+
if (innerType !== null)
|
|
867
|
+
return extractAdlTypeNamespace(innerType);
|
|
868
|
+
for (let i = 0; i < typeNode.childCount; i++) {
|
|
869
|
+
const child = typeNode.child(i);
|
|
870
|
+
if (child === null)
|
|
871
|
+
continue;
|
|
872
|
+
if (child.type === 'qualified_identifier' ||
|
|
873
|
+
child.type === 'template_type' ||
|
|
874
|
+
child.type === 'type_identifier') {
|
|
875
|
+
return extractAdlTypeNamespace(child);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return '';
|
|
879
|
+
}
|
|
880
|
+
if (typeNode.type === 'template_type') {
|
|
881
|
+
const nameNode = typeNode.childForFieldName('name');
|
|
882
|
+
return nameNode !== null ? extractAdlTypeNamespace(nameNode) : '';
|
|
883
|
+
}
|
|
884
|
+
if (typeNode.type === 'qualified_identifier') {
|
|
885
|
+
const scope = typeNode.childForFieldName('scope');
|
|
886
|
+
if (scope !== null)
|
|
887
|
+
return normalizeCppNamespaceQName(scope.text);
|
|
888
|
+
return extractNamespaceFromQualifiedText(typeNode.text);
|
|
889
|
+
}
|
|
809
890
|
return '';
|
|
810
891
|
}
|
|
892
|
+
function extractAdlTemplateInfo(typeNode) {
|
|
893
|
+
const templateTypeNode = findTemplateTypeNode(typeNode);
|
|
894
|
+
if (templateTypeNode === null) {
|
|
895
|
+
return {
|
|
896
|
+
templateSimpleClassName: '',
|
|
897
|
+
templateNamespace: '',
|
|
898
|
+
templateArgClassNames: [],
|
|
899
|
+
templateArgNamespaces: [],
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const templateArgClassNames = [];
|
|
903
|
+
const templateArgNamespaces = [];
|
|
904
|
+
collectAdlTemplateArgs(templateTypeNode, 0, templateArgClassNames, templateArgNamespaces);
|
|
905
|
+
return {
|
|
906
|
+
templateSimpleClassName: extractAdlSimpleTypeName(templateTypeNode),
|
|
907
|
+
templateNamespace: extractAdlTypeNamespace(typeNode),
|
|
908
|
+
templateArgClassNames,
|
|
909
|
+
templateArgNamespaces,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
function collectAdlTemplateArgs(templateTypeNode, depth, outClassNames, outNamespaces) {
|
|
913
|
+
if (depth >= ADL_TEMPLATE_RECURSION_MAX_DEPTH)
|
|
914
|
+
return;
|
|
915
|
+
if (templateTypeNode.type !== 'template_type')
|
|
916
|
+
return;
|
|
917
|
+
const argList = templateTypeNode.childForFieldName('arguments') ??
|
|
918
|
+
findChildOfType(templateTypeNode, ['template_argument_list']);
|
|
919
|
+
if (argList === null)
|
|
920
|
+
return;
|
|
921
|
+
for (let i = 0; i < argList.namedChildCount; i++) {
|
|
922
|
+
const arg = argList.namedChild(i);
|
|
923
|
+
if (arg === null || arg.type !== 'type_descriptor')
|
|
924
|
+
continue;
|
|
925
|
+
const simpleClassName = extractAdlSimpleTypeName(arg);
|
|
926
|
+
if (simpleClassName.length > 0)
|
|
927
|
+
outClassNames.push(simpleClassName);
|
|
928
|
+
const ns = extractAdlTypeNamespace(arg);
|
|
929
|
+
if (ns.length > 0)
|
|
930
|
+
outNamespaces.push(ns);
|
|
931
|
+
const nestedType = arg.childForFieldName('type');
|
|
932
|
+
const nestedTemplate = nestedType !== null ? findTemplateTypeNode(nestedType) : null;
|
|
933
|
+
if (nestedTemplate !== null) {
|
|
934
|
+
collectAdlTemplateArgs(nestedTemplate, depth + 1, outClassNames, outNamespaces);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function findTemplateTypeNode(typeNode) {
|
|
939
|
+
if (typeNode.type === 'template_type')
|
|
940
|
+
return typeNode;
|
|
941
|
+
if (typeNode.type === 'type_descriptor') {
|
|
942
|
+
const innerType = typeNode.childForFieldName('type');
|
|
943
|
+
if (innerType !== null)
|
|
944
|
+
return findTemplateTypeNode(innerType);
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
if (typeNode.type === 'qualified_identifier') {
|
|
948
|
+
const nameNode = typeNode.childForFieldName('name');
|
|
949
|
+
if (nameNode !== null)
|
|
950
|
+
return findTemplateTypeNode(nameNode);
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
return null;
|
|
954
|
+
}
|
|
955
|
+
function normalizeCppNamespaceQName(text) {
|
|
956
|
+
const normalized = text.replace(/^::/, '').replace(/::$/, '').replace(/::/g, '.');
|
|
957
|
+
return normalized;
|
|
958
|
+
}
|
|
959
|
+
function extractNamespaceFromQualifiedText(text) {
|
|
960
|
+
const cleaned = text.replace(/\s+/g, '');
|
|
961
|
+
const idx = cleaned.lastIndexOf('::');
|
|
962
|
+
if (idx <= 0)
|
|
963
|
+
return '';
|
|
964
|
+
return normalizeCppNamespaceQName(cleaned.slice(0, idx));
|
|
965
|
+
}
|
|
811
966
|
/**
|
|
812
967
|
* Check if a C++ function_definition or declaration has `static` storage class.
|
|
813
968
|
*/
|
|
@@ -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.
|
|
@@ -188,8 +189,10 @@ export const cppScopeResolver = {
|
|
|
188
189
|
// C++ argument-dependent / Koenig lookup (U2 of plan 2026-05-13-001).
|
|
189
190
|
// Fires after `findCallableBindingInScope` returns undefined; surfaces
|
|
190
191
|
// candidates from the associated namespaces of class-typed arguments.
|
|
191
|
-
//
|
|
192
|
-
//
|
|
192
|
+
// Current boundary: class-typed value/pointer/reference args and template
|
|
193
|
+
// specializations with explicit type arguments contribute associated
|
|
194
|
+
// namespaces. Function-pointer args, base-class associated namespaces,
|
|
195
|
+
// and full ordinary+ADL merge remain excluded.
|
|
193
196
|
resolveAdlCandidates: (site, callerParsed, scopes, parsedFiles) => {
|
|
194
197
|
// `using ns::name;` introduces `name` into ordinary unqualified lookup.
|
|
195
198
|
// For template-class method bodies, lexical scope walks can miss this
|
package/package.json
CHANGED