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.
@@ -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`, `audit::Event** pp`):
22
- * they contribute the pointee class's enclosing namespace too. Reference
23
- * arguments, function-pointer arguments, template specializations,
24
- * base-class associated namespaces, and the rest of the full closure are
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 !== ''` AND `!isReference`, including
60
- * class pointers whose declarator chain resolves to a named class type.
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 or
110
- * pointer, but not reference, function pointer, literal, or primitive).
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`, `audit::Event** pp`):
22
- * they contribute the pointee class's enclosing namespace too. Reference
23
- * arguments, function-pointer arguments, template specializations,
24
- * base-class associated namespaces, and the rest of the full closure are
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 or
123
- * pointer, but not reference, function pointer, literal, or primitive).
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: '', isPointer: false, isReference: false };
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 > ...` means
733
- // reference-typed; bare `init_declarator > identifier` is value.
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, isPointer, isReference };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.5-rc.38",
3
+ "version": "1.6.5-rc.39",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",