gitnexus 1.6.5-rc.38 → 1.6.5-rc.39
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 +9 -14
- package/dist/core/ingestion/languages/cpp/adl.js +7 -9
- package/dist/core/ingestion/languages/cpp/captures.js +6 -9
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +31 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +11 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +66 -0
- package/package.json +1 -1
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
* The current implementation covers ONE associated-entity rule: an argument that's a directly-named
|
|
19
19
|
* class type (`audit::Event e`) contributes its **direct enclosing
|
|
20
20
|
* namespace** to the candidate set. V2 extends that one step to
|
|
21
|
-
* pointer-typed class args (`audit::Event* p`,
|
|
22
|
-
* they contribute the pointee
|
|
23
|
-
*
|
|
24
|
-
* base-class associated namespaces, and the
|
|
25
|
-
* still deliberately excluded.
|
|
21
|
+
* pointer-typed and reference-typed class args (`audit::Event* p`,
|
|
22
|
+
* `audit::Event& r`, `audit::Event&& rr`): they contribute the pointee /
|
|
23
|
+
* referred class's enclosing namespace too. Function-pointer arguments,
|
|
24
|
+
* template specializations, base-class associated namespaces, and the
|
|
25
|
+
* rest of the full closure are still deliberately excluded.
|
|
26
26
|
*
|
|
27
27
|
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
28
28
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
@@ -56,18 +56,13 @@ import type { ParsedFile, SymbolDefinition } from '../../../../_shared/index.js'
|
|
|
56
56
|
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
57
57
|
/**
|
|
58
58
|
* Per-argument shape information collected at capture time. ADL fires for
|
|
59
|
-
* arguments where `simpleClassName !== ''
|
|
60
|
-
*
|
|
59
|
+
* arguments where `simpleClassName !== ''`, including class pointers and
|
|
60
|
+
* references whose declarator chain resolves to a named class type.
|
|
61
61
|
*/
|
|
62
62
|
export interface CppAdlArgInfo {
|
|
63
63
|
/** Simple class-like type name (last segment of qualified name); empty
|
|
64
64
|
* for primitives, literals, function pointers, template specs, etc. */
|
|
65
65
|
readonly simpleClassName: string;
|
|
66
|
-
/** True when the variable's declarator contained one or more
|
|
67
|
-
* `pointer_declarator` wrappers. */
|
|
68
|
-
readonly isPointer: boolean;
|
|
69
|
-
/** True when the variable's declarator was a `reference_declarator`. */
|
|
70
|
-
readonly isReference: boolean;
|
|
71
66
|
}
|
|
72
67
|
/** Sentinel returned by `pickCppAdlCandidates` when ADL surfaces multiple
|
|
73
68
|
* candidates that share normalized parameter types — the caller MUST
|
|
@@ -106,8 +101,8 @@ export declare function populateCppAssociatedNamespaces(parsed: ParsedFile): voi
|
|
|
106
101
|
*
|
|
107
102
|
* Fires only when:
|
|
108
103
|
* - the call site is not in `noAdlSites` (parenthesized form), AND
|
|
109
|
-
* - at least one argument resolves to a named class type (value
|
|
110
|
-
* pointer, but not
|
|
104
|
+
* - at least one argument resolves to a named class type (value,
|
|
105
|
+
* pointer, or reference; but not function pointer, literal, or primitive).
|
|
111
106
|
*/
|
|
112
107
|
export declare function pickCppAdlCandidates(site: {
|
|
113
108
|
readonly name: string;
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
* The current implementation covers ONE associated-entity rule: an argument that's a directly-named
|
|
19
19
|
* class type (`audit::Event e`) contributes its **direct enclosing
|
|
20
20
|
* namespace** to the candidate set. V2 extends that one step to
|
|
21
|
-
* pointer-typed class args (`audit::Event* p`,
|
|
22
|
-
* they contribute the pointee
|
|
23
|
-
*
|
|
24
|
-
* base-class associated namespaces, and the
|
|
25
|
-
* still deliberately excluded.
|
|
21
|
+
* pointer-typed and reference-typed class args (`audit::Event* p`,
|
|
22
|
+
* `audit::Event& r`, `audit::Event&& rr`): they contribute the pointee /
|
|
23
|
+
* referred class's enclosing namespace too. Function-pointer arguments,
|
|
24
|
+
* template specializations, base-class associated namespaces, and the
|
|
25
|
+
* rest of the full closure are still deliberately excluded.
|
|
26
26
|
*
|
|
27
27
|
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
28
28
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
@@ -119,8 +119,8 @@ export function populateCppAssociatedNamespaces(parsed) {
|
|
|
119
119
|
*
|
|
120
120
|
* Fires only when:
|
|
121
121
|
* - the call site is not in `noAdlSites` (parenthesized form), AND
|
|
122
|
-
* - at least one argument resolves to a named class type (value
|
|
123
|
-
* pointer, but not
|
|
122
|
+
* - at least one argument resolves to a named class type (value,
|
|
123
|
+
* pointer, or reference; but not function pointer, literal, or primitive).
|
|
124
124
|
*/
|
|
125
125
|
export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
126
126
|
const key = siteKey(callerParsed.filePath, site.atRange.startLine, site.atRange.startCol);
|
|
@@ -134,8 +134,6 @@ export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
|
134
134
|
for (const arg of args) {
|
|
135
135
|
if (arg.simpleClassName === '')
|
|
136
136
|
continue;
|
|
137
|
-
if (arg.isReference)
|
|
138
|
-
continue;
|
|
139
137
|
const classDef = findCppClassDefBySimpleName(arg.simpleClassName, scopes);
|
|
140
138
|
if (classDef === undefined)
|
|
141
139
|
continue;
|
|
@@ -682,7 +682,7 @@ function inferCppCallAdlArgs(callNode) {
|
|
|
682
682
|
}
|
|
683
683
|
return out;
|
|
684
684
|
}
|
|
685
|
-
const EMPTY_ADL_ARG = { simpleClassName: ''
|
|
685
|
+
const EMPTY_ADL_ARG = { simpleClassName: '' };
|
|
686
686
|
function classifyAdlArg(argNode) {
|
|
687
687
|
// Literals and primitive-shaped expressions never have associated namespaces.
|
|
688
688
|
if (argNode.type === 'number_literal' ||
|
|
@@ -729,12 +729,11 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
729
729
|
// Unwrap declarator chain to find pointer/reference markers and the
|
|
730
730
|
// variable name. `init_declarator > pointer_declarator > identifier`
|
|
731
731
|
// means pointer-typed; repeated pointer wrappers still count as pointer
|
|
732
|
-
// typed; `init_declarator > reference_declarator > ...`
|
|
733
|
-
// reference-typed; bare
|
|
732
|
+
// typed; `init_declarator > reference_declarator > ...` (or
|
|
733
|
+
// `rvalue_reference_declarator`) means reference-typed; bare
|
|
734
|
+
// `init_declarator > identifier` is value.
|
|
734
735
|
// Function-pointer wrappers (`pointer_declarator > function_declarator`)
|
|
735
736
|
// must not contribute ADL associated namespaces.
|
|
736
|
-
let isPointer = false;
|
|
737
|
-
let isReference = false;
|
|
738
737
|
let isFunctionPointer = false;
|
|
739
738
|
let inner = declarator;
|
|
740
739
|
let nameText = null;
|
|
@@ -745,15 +744,13 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
745
744
|
isFunctionPointer = true;
|
|
746
745
|
break;
|
|
747
746
|
}
|
|
748
|
-
isPointer = true;
|
|
749
747
|
const next = inner.childForFieldName('declarator');
|
|
750
748
|
if (next === null)
|
|
751
749
|
break;
|
|
752
750
|
inner = next;
|
|
753
751
|
continue;
|
|
754
752
|
}
|
|
755
|
-
if (inner.type === 'reference_declarator') {
|
|
756
|
-
isReference = true;
|
|
753
|
+
if (inner.type === 'reference_declarator' || inner.type === 'rvalue_reference_declarator') {
|
|
757
754
|
// reference_declarator has a single child (the inner declarator).
|
|
758
755
|
let next = null;
|
|
759
756
|
for (let j = 0; j < inner.namedChildCount; j++) {
|
|
@@ -786,7 +783,7 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
786
783
|
if (isFunctionPointer || nameText !== varName)
|
|
787
784
|
continue;
|
|
788
785
|
const simpleClassName = extractAdlSimpleTypeName(typeNode);
|
|
789
|
-
return { simpleClassName
|
|
786
|
+
return { simpleClassName };
|
|
790
787
|
}
|
|
791
788
|
return EMPTY_ADL_ARG;
|
|
792
789
|
}
|
|
@@ -12,6 +12,7 @@ import { populateCppDependentBases, clearCppDependentBases, isCppDependentBaseMe
|
|
|
12
12
|
import { populateCppAssociatedNamespaces, clearCppAdlState, pickCppAdlCandidates, ADL_AMBIGUOUS, } from './adl.js';
|
|
13
13
|
import { clearCppInlineNamespaces, populateCppInlineNamespaceScopes, resolveCppQualifiedNamespaceMember, } from './inline-namespaces.js';
|
|
14
14
|
import { populateCppRangeBindings } from './range-bindings.js';
|
|
15
|
+
import { isOverloadAmbiguousAfterNormalization, narrowOverloadCandidates, } from '../../scope-resolution/passes/overload-narrowing.js';
|
|
15
16
|
/**
|
|
16
17
|
* C++ `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
|
|
17
18
|
* the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
|
|
@@ -145,6 +146,8 @@ export const cppScopeResolver = {
|
|
|
145
146
|
// for cross-file propagation and compound-receiver chain resolution.
|
|
146
147
|
// cppBindingScopeFor hoists @type-binding.return to Module scope.
|
|
147
148
|
hoistTypeBindingsToModule: true,
|
|
149
|
+
// Enable receiver-bound explicit-`this` fallback only for C++.
|
|
150
|
+
resolveThisViaEnclosingClass: true,
|
|
148
151
|
// The `isFileLocalDef` hook on the global free-call fallback names
|
|
149
152
|
// file-local linkage historically, but semantically gates "logically
|
|
150
153
|
// invisible cross-file" defs. C++ extends this to also reject class-
|
|
@@ -188,6 +191,34 @@ export const cppScopeResolver = {
|
|
|
188
191
|
// V1 limitation: only direct enclosing-namespace closure for value
|
|
189
192
|
// class-typed args; pointer/reference/template-spec args excluded.
|
|
190
193
|
resolveAdlCandidates: (site, callerParsed, scopes, parsedFiles) => {
|
|
194
|
+
// `using ns::name;` introduces `name` into ordinary unqualified lookup.
|
|
195
|
+
// For template-class method bodies, lexical scope walks can miss this
|
|
196
|
+
// named-using visibility; recover by resolving the imported namespace
|
|
197
|
+
// member directly when the local call name matches a named using import.
|
|
198
|
+
const usingNamedHits = [];
|
|
199
|
+
const seenUsing = new Set();
|
|
200
|
+
for (const imp of callerParsed.parsedImports) {
|
|
201
|
+
if (imp.kind !== 'named')
|
|
202
|
+
continue;
|
|
203
|
+
if (imp.localName !== site.name)
|
|
204
|
+
continue;
|
|
205
|
+
const member = resolveCppQualifiedNamespaceMember(imp.targetRaw, imp.importedName, parsedFiles, scopes);
|
|
206
|
+
if (member === undefined)
|
|
207
|
+
continue;
|
|
208
|
+
if (seenUsing.has(member.nodeId))
|
|
209
|
+
continue;
|
|
210
|
+
seenUsing.add(member.nodeId);
|
|
211
|
+
usingNamedHits.push(member);
|
|
212
|
+
}
|
|
213
|
+
if (usingNamedHits.length > 0) {
|
|
214
|
+
const narrowed = narrowOverloadCandidates(usingNamedHits, site.arity, site.argumentTypes);
|
|
215
|
+
if (isOverloadAmbiguousAfterNormalization(narrowed, site.arity))
|
|
216
|
+
return 'ambiguous';
|
|
217
|
+
if (narrowed.length === 1)
|
|
218
|
+
return narrowed[0];
|
|
219
|
+
if (narrowed.length > 1)
|
|
220
|
+
return 'ambiguous';
|
|
221
|
+
}
|
|
191
222
|
const result = pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles);
|
|
192
223
|
if (result === ADL_AMBIGUOUS)
|
|
193
224
|
return 'ambiguous';
|
|
@@ -547,6 +547,17 @@ export interface ScopeResolver {
|
|
|
547
547
|
* remaining cases.
|
|
548
548
|
*/
|
|
549
549
|
readonly resolveQualifiedReceiverMember?: (receiverName: string, memberName: string, callerScope: ScopeId, scopes: ScopeResolutionIndexes, parsedFiles: readonly ParsedFile[]) => SymbolDefinition | undefined;
|
|
550
|
+
/**
|
|
551
|
+
* Enable the receiver-bound Case 0.5 fallback for explicit `this`
|
|
552
|
+
* receivers (`this->m()` / `this.m()`) that resolves against the
|
|
553
|
+
* enclosing class + MRO even when no explicit `this` typeBinding is
|
|
554
|
+
* present in scope.
|
|
555
|
+
*
|
|
556
|
+
* Keep disabled for languages where the existing type-binding path
|
|
557
|
+
* (Case 4) already handles `this` correctly and overload ambiguity
|
|
558
|
+
* suppression must remain unchanged.
|
|
559
|
+
*/
|
|
560
|
+
readonly resolveThisViaEnclosingClass?: boolean;
|
|
550
561
|
/**
|
|
551
562
|
* Optional post-finalize hook to inject cross-file bindings that
|
|
552
563
|
* aren't modeled via explicit imports. Runs after
|
|
@@ -41,7 +41,7 @@ import type { WorkspaceResolutionIndex } from '../workspace-index.js';
|
|
|
41
41
|
/** Subset of `ScopeResolver` consumed by this pass. Accepting the
|
|
42
42
|
* subset rather than the full provider keeps tests and partial
|
|
43
43
|
* refactors lighter — callers only need to populate what we read. */
|
|
44
|
-
type ReceiverBoundProviderSubset = Pick<ScopeResolver, 'isSuperReceiver' | 'isSuperReceiverInContext' | 'fieldFallbackOnMethodLookup' | 'collapseMemberCallsByCallerTarget' | 'unwrapCollectionAccessor' | 'hoistTypeBindingsToModule' | 'resolveQualifiedReceiverMember'>;
|
|
44
|
+
type ReceiverBoundProviderSubset = Pick<ScopeResolver, 'isSuperReceiver' | 'isSuperReceiverInContext' | 'fieldFallbackOnMethodLookup' | 'collapseMemberCallsByCallerTarget' | 'unwrapCollectionAccessor' | 'hoistTypeBindingsToModule' | 'resolveQualifiedReceiverMember' | 'resolveThisViaEnclosingClass'>;
|
|
45
45
|
export declare function emitReceiverBoundCalls(graph: KnowledgeGraph, scopes: ScopeResolutionIndexes, parsedFiles: readonly ParsedFile[], nodeLookup: GraphNodeLookup, handledSites: Set<string>, provider: ReceiverBoundProviderSubset, index: WorkspaceResolutionIndex, model: SemanticModel): number;
|
|
46
46
|
/**
|
|
47
47
|
* Sentinel returned by `pickOverload` when narrowing leaves >1 candidate
|
|
@@ -232,6 +232,72 @@ export function emitReceiverBoundCalls(graph, scopes, parsedFiles, nodeLookup, h
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
+
// ── Case 0.5: implicit `this` receiver ───────────────────────
|
|
236
|
+
// C++ `this->member()` (and same-shape receivers in other OO
|
|
237
|
+
// languages) should resolve against the enclosing class + MRO
|
|
238
|
+
// even when there is no explicit `this` typeBinding in scope.
|
|
239
|
+
if (provider.resolveThisViaEnclosingClass === true && receiverName === 'this') {
|
|
240
|
+
const enclosingClass = findEnclosingClassDef(site.inScope, scopes);
|
|
241
|
+
if (enclosingClass !== undefined) {
|
|
242
|
+
const chain = [
|
|
243
|
+
enclosingClass.nodeId,
|
|
244
|
+
...scopes.methodDispatch.mroFor(enclosingClass.nodeId),
|
|
245
|
+
];
|
|
246
|
+
let memberDef;
|
|
247
|
+
let ambiguous = false;
|
|
248
|
+
let hiddenByName = false;
|
|
249
|
+
for (const ownerId of chain) {
|
|
250
|
+
const methodOverloads = model.methods.lookupAllByOwner(ownerId, memberName);
|
|
251
|
+
if (methodOverloads.length > 0) {
|
|
252
|
+
const narrowed = narrowOverloadCandidates(methodOverloads, site.arity, site.argumentTypes);
|
|
253
|
+
if (isOverloadAmbiguousAfterNormalization(narrowed, site.arity)) {
|
|
254
|
+
ambiguous = true;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
if (narrowed.length === 0) {
|
|
258
|
+
// C++ name hiding: if the derived class declares `f`, base-class
|
|
259
|
+
// overloads named `f` are hidden for member lookup
|
|
260
|
+
// ([basic.lookup.classref]). A non-viable derived overload set
|
|
261
|
+
// therefore terminates lookup instead of falling through to base.
|
|
262
|
+
hiddenByName = true;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
memberDef = narrowed[0] ?? methodOverloads[0];
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
// Field/property lookup intentionally runs only after the method
|
|
269
|
+
// lookup above: in C++ member-name lookup, functions with this
|
|
270
|
+
// name hide same-named base members; we therefore prefer method
|
|
271
|
+
// candidates first and only target a field when no methods with
|
|
272
|
+
// this name exist on the current owner.
|
|
273
|
+
memberDef = model.fields.lookupFieldByOwner(ownerId, memberName);
|
|
274
|
+
if (memberDef !== undefined) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (ambiguous) {
|
|
279
|
+
handledSites.add(siteKey);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (hiddenByName) {
|
|
283
|
+
handledSites.add(siteKey);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (memberDef !== undefined) {
|
|
287
|
+
const reason = site.kind === 'write' || site.kind === 'read'
|
|
288
|
+
? site.kind
|
|
289
|
+
: memberDef.filePath !== parsed.filePath
|
|
290
|
+
? 'import-resolved'
|
|
291
|
+
: 'global';
|
|
292
|
+
const confidence = site.kind === 'write' || site.kind === 'read' ? 1.0 : 0.85;
|
|
293
|
+
const ok = tryEmitEdge(graph, scopes, nodeLookup, site, memberDef, reason, seen, confidence, collapse);
|
|
294
|
+
if (ok)
|
|
295
|
+
emitted++;
|
|
296
|
+
handledSites.add(siteKey);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
235
301
|
// ── Case 1: namespace receiver ───────────────────────────────
|
|
236
302
|
const targetFiles = namespaceTargets.get(receiverName);
|
|
237
303
|
if (targetFiles !== undefined) {
|
package/package.json
CHANGED