gitnexus 1.6.6-rc.21 → 1.6.6-rc.22

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.
@@ -1,31 +1,29 @@
1
1
  /**
2
- * C++ conversion-rank scoring for overload resolution (#1578).
2
+ * C++ conversion-rank scoring for overload resolution (#1578, #1637).
3
3
  *
4
- * Operates on **normalized** type strings (output of
5
- * `normalizeCppParamType` in `arity-metadata.ts`). After normalization:
6
- * - int/long/short/unsigned 'int'
7
- * - float/double 'double'
8
- * - char → 'char', bool → 'bool'
9
- *
10
- * Because the normalizer collapses promotion pairs (int↔long,
11
- * float↔double) to the same string, those promotions are invisible at
12
- * this layer — they appear as exact matches (rank 0).
4
+ * Operates on normalized type strings (output of `normalizeCppParamType`
5
+ * in `arity-metadata.ts`) plus optional shape sidecars from #1630.
6
+ * Normalization intentionally collapses cv/ref/pointer spelling for stable
7
+ * graph IDs, so pointer/nullptr rules must consult `ParameterTypeClass`.
13
8
  *
14
9
  * Post-normalization ranking:
15
- * - rank 0 exact (same normalized type)
16
- * - rank 1 integral promotion (charint, boolint)
17
- * - rank 2 standard arithmetic conversion (int↔double, char→double,
18
- * bool→double)
19
- * - Infinity mismatch (string↔int, user types, pointers, etc.)
10
+ * - rank 0: exact (same normalized type)
11
+ * - rank 1: integral promotion (char -> int, bool -> int)
12
+ * - rank 2: standard conversion (arithmetic, nullptr -> T*, T* -> bool,
13
+ * T* -> void*)
14
+ * - rank 3: nullptr -> bool (kept worse than nullptr -> T*)
15
+ * - rank 4: ellipsis conversion (worst viable)
16
+ * - Infinity: mismatch (string -> int, user types, unsupported shapes)
20
17
  *
21
- * This function is intentionally C++-specific (issue #1578 pitfall:
22
- * keep conversion-rank tables out of shared overload-narrowing). Other
23
- * languages may define their own `ConversionRankFn` in the future.
18
+ * This function is intentionally C++-specific. Other languages may define
19
+ * their own `ConversionRankFn` in the future.
24
20
  */
21
+ import type { ParameterTypeClass } from '../../../../_shared/index.js';
25
22
  /**
26
23
  * Return the conversion rank from `argType` to `paramType`.
27
24
  *
28
- * @returns 0 for exact match, 1 for integral promotion (char/bool→int),
29
- * 2 for standard arithmetic conversion, Infinity for mismatch.
25
+ * @returns 0 for exact match, 1 for integral promotion, 2 for standard
26
+ * conversion, 3 for nullptr -> bool, 4 for ellipsis, Infinity
27
+ * for mismatch.
30
28
  */
31
- export declare function cppConversionRank(argType: string, paramType: string): number;
29
+ export declare function cppConversionRank(argType: string, paramType: string, argTypeClass?: ParameterTypeClass, paramTypeClass?: ParameterTypeClass): number;
@@ -1,30 +1,26 @@
1
1
  /**
2
- * C++ conversion-rank scoring for overload resolution (#1578).
2
+ * C++ conversion-rank scoring for overload resolution (#1578, #1637).
3
3
  *
4
- * Operates on **normalized** type strings (output of
5
- * `normalizeCppParamType` in `arity-metadata.ts`). After normalization:
6
- * - int/long/short/unsigned 'int'
7
- * - float/double 'double'
8
- * - char → 'char', bool → 'bool'
9
- *
10
- * Because the normalizer collapses promotion pairs (int↔long,
11
- * float↔double) to the same string, those promotions are invisible at
12
- * this layer — they appear as exact matches (rank 0).
4
+ * Operates on normalized type strings (output of `normalizeCppParamType`
5
+ * in `arity-metadata.ts`) plus optional shape sidecars from #1630.
6
+ * Normalization intentionally collapses cv/ref/pointer spelling for stable
7
+ * graph IDs, so pointer/nullptr rules must consult `ParameterTypeClass`.
13
8
  *
14
9
  * Post-normalization ranking:
15
- * - rank 0 exact (same normalized type)
16
- * - rank 1 integral promotion (charint, boolint)
17
- * - rank 2 standard arithmetic conversion (int↔double, char→double,
18
- * bool→double)
19
- * - Infinity mismatch (string↔int, user types, pointers, etc.)
10
+ * - rank 0: exact (same normalized type)
11
+ * - rank 1: integral promotion (char -> int, bool -> int)
12
+ * - rank 2: standard conversion (arithmetic, nullptr -> T*, T* -> bool,
13
+ * T* -> void*)
14
+ * - rank 3: nullptr -> bool (kept worse than nullptr -> T*)
15
+ * - rank 4: ellipsis conversion (worst viable)
16
+ * - Infinity: mismatch (string -> int, user types, unsupported shapes)
20
17
  *
21
- * This function is intentionally C++-specific (issue #1578 pitfall:
22
- * keep conversion-rank tables out of shared overload-narrowing). Other
23
- * languages may define their own `ConversionRankFn` in the future.
18
+ * This function is intentionally C++-specific. Other languages may define
19
+ * their own `ConversionRankFn` in the future.
24
20
  */
25
21
  /** Set of normalized arithmetic types that support implicit conversion. */
26
22
  const ARITHMETIC = new Set(['int', 'double', 'char', 'bool']);
27
- /** Integral promotion targets: charint and boolint are rank 1. */
23
+ /** Integral promotion targets: char -> int and bool -> int are rank 1. */
28
24
  const INTEGRAL_PROMOTION = new Map([
29
25
  ['char', 'int'],
30
26
  ['bool', 'int'],
@@ -32,16 +28,38 @@ const INTEGRAL_PROMOTION = new Map([
32
28
  /**
33
29
  * Return the conversion rank from `argType` to `paramType`.
34
30
  *
35
- * @returns 0 for exact match, 1 for integral promotion (char/bool→int),
36
- * 2 for standard arithmetic conversion, Infinity for mismatch.
31
+ * @returns 0 for exact match, 1 for integral promotion, 2 for standard
32
+ * conversion, 3 for nullptr -> bool, 4 for ellipsis, Infinity
33
+ * for mismatch.
37
34
  */
38
- export function cppConversionRank(argType, paramType) {
39
- if (argType === paramType)
40
- return 0;
41
- // Integral promotions: char→int, bool→int (ISO C++ [conv.prom])
35
+ export function cppConversionRank(argType, paramType, argTypeClass, paramTypeClass) {
36
+ if (argType === paramType) {
37
+ return exactShapeCompatible(argTypeClass, paramTypeClass) ? 0 : Infinity;
38
+ }
39
+ if (paramType === '...')
40
+ return 4;
42
41
  if (INTEGRAL_PROMOTION.get(argType) === paramType)
43
42
  return 1;
44
43
  if (ARITHMETIC.has(argType) && ARITHMETIC.has(paramType))
45
44
  return 2;
45
+ if (argType === 'null' && isPointer(paramTypeClass))
46
+ return 2;
47
+ if (argType === 'null' && paramType === 'bool')
48
+ return 3;
49
+ if (isPointer(argTypeClass) && paramType === 'bool')
50
+ return 2;
51
+ if (isPointer(argTypeClass) && isPointer(paramTypeClass) && paramType === 'void')
52
+ return 2;
46
53
  return Infinity;
47
54
  }
55
+ function isPointer(typeClass) {
56
+ return typeClass?.indirection === 'pointer' && typeClass.pointerDepth > 0;
57
+ }
58
+ function exactShapeCompatible(argTypeClass, paramTypeClass) {
59
+ if (argTypeClass === undefined || paramTypeClass === undefined)
60
+ return true;
61
+ if (argTypeClass.indirection === 'unknown' || paramTypeClass.indirection === 'unknown') {
62
+ return true;
63
+ }
64
+ return isPointer(argTypeClass) === isPointer(paramTypeClass);
65
+ }
@@ -202,7 +202,7 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
202
202
  callerScope: site.inScope,
203
203
  scopes,
204
204
  })
205
- : undefined, site.argumentTypes, options.conversionRankFn);
205
+ : undefined, site.argumentTypes, site.argumentTypeClasses, options.conversionRankFn);
206
206
  }
207
207
  if (fnDef === undefined)
208
208
  continue;
@@ -261,7 +261,7 @@ function buildGlobalCallableIndex(scopes) {
261
261
  }
262
262
  return out;
263
263
  }
264
- function pickUniqueGlobalCallable(name, model, globalCallablesBySimpleName, callerFilePath, isFileLocalDef, callArity, isCallerVisible, callArgTypes, conversionRankFn) {
264
+ function pickUniqueGlobalCallable(name, model, globalCallablesBySimpleName, callerFilePath, isFileLocalDef, callArity, isCallerVisible, callArgTypes, callArgTypeClasses, conversionRankFn) {
265
265
  const scopeDefs = [];
266
266
  const scopeSeen = new Set();
267
267
  for (const def of globalCallablesBySimpleName.get(name) ?? []) {
@@ -300,6 +300,7 @@ function pickUniqueGlobalCallable(name, model, globalCallablesBySimpleName, call
300
300
  // disambiguate (e.g., `f(int)` vs `f(double)` called with `f(2.5)`).
301
301
  if (scopeDefs.length > 1) {
302
302
  const narrowed = narrowOverloadCandidates(scopeDefs, callArity, callArgTypes, {
303
+ argumentTypeClasses: callArgTypeClasses,
303
304
  conversionRankFn,
304
305
  });
305
306
  if (narrowed.length === 1)
@@ -340,6 +341,7 @@ function pickUniqueGlobalCallable(name, model, globalCallablesBySimpleName, call
340
341
  // Same argument-type + conversion-rank narrowing for the model pool.
341
342
  if (defs.length > 1) {
342
343
  const narrowed = narrowOverloadCandidates(defs, callArity, callArgTypes, {
344
+ argumentTypeClasses: callArgTypeClasses,
343
345
  conversionRankFn,
344
346
  });
345
347
  if (narrowed.length === 1)
@@ -37,7 +37,7 @@
37
37
  * (monotonicity).
38
38
  * 5. Empty input returns empty output.
39
39
  */
40
- import type { ArityVerdict, Callsite, ConstraintContext, SymbolDefinition } from '../../../../_shared/index.js';
40
+ import type { ArityVerdict, Callsite, ConstraintContext, ParameterTypeClass, SymbolDefinition } from '../../../../_shared/index.js';
41
41
  /**
42
42
  * Per-slot conversion-rank function. Returns a numeric cost for
43
43
  * converting `argType` to `paramType`:
@@ -49,7 +49,7 @@ import type { ArityVerdict, Callsite, ConstraintContext, SymbolDefinition } from
49
49
  * Each language provides its own implementation. The function operates
50
50
  * on normalized type strings (output of the language's type normalizer).
51
51
  */
52
- export type ConversionRankFn = (argType: string, paramType: string) => number;
52
+ export type ConversionRankFn = (argType: string, paramType: string, argTypeClass?: ParameterTypeClass, paramTypeClass?: ParameterTypeClass) => number;
53
53
  /**
54
54
  * Optional hook bundle for narrowing extension points. Threaded in
55
55
  * from `pickOverload` / `pickImplicitThisOverload` so per-language
@@ -81,8 +81,9 @@ export function narrowOverloadCandidates(overloads, argCount, argTypes, hookCtx)
81
81
  for (let i = 0; i < argTypes.length && i < params.length; i++) {
82
82
  if (argTypes[i] === '')
83
83
  continue;
84
- if (argTypes[i] !== params[i])
84
+ if (!exactTypeSlotMatches(argTypes[i], params[i], hookCtx?.argumentTypeClasses?.[i], d.parameterTypeClasses?.[i])) {
85
85
  return false;
86
+ }
86
87
  }
87
88
  return true;
88
89
  });
@@ -97,7 +98,7 @@ export function narrowOverloadCandidates(overloads, argCount, argTypes, hookCtx)
97
98
  // are returned; multiple survivors are genuinely ambiguous. When
98
99
  // ranking also yields empty, fall through to the arity-filtered
99
100
  // `candidates` set — matches pre-#1606 behavior.
100
- const ranked = rankByConversion(candidates, argTypes, hookCtx.conversionRankFn);
101
+ const ranked = rankByConversion(candidates, argTypes, hookCtx.conversionRankFn, hookCtx.argumentTypeClasses);
101
102
  if (ranked.length > 0)
102
103
  result = ranked;
103
104
  }
@@ -134,6 +135,22 @@ export function narrowOverloadCandidates(overloads, argCount, argTypes, hookCtx)
134
135
  }
135
136
  return result;
136
137
  }
138
+ function exactTypeSlotMatches(argType, paramType, argTypeClass, paramTypeClass) {
139
+ if (argType !== paramType)
140
+ return false;
141
+ // C++ normalizes away pointer markers (`int*` -> `int`). When both sides
142
+ // provide shape sidecars, do not let that collapse make `int` exactly match
143
+ // `int*`. Unknown sidecar evidence preserves the previous string-only path.
144
+ if (argTypeClass === undefined || paramTypeClass === undefined)
145
+ return true;
146
+ if (argTypeClass.indirection === 'unknown' || paramTypeClass.indirection === 'unknown') {
147
+ return true;
148
+ }
149
+ return isPointerShape(argTypeClass) === isPointerShape(paramTypeClass);
150
+ }
151
+ function isPointerShape(typeClass) {
152
+ return typeClass.indirection === 'pointer' && typeClass.pointerDepth > 0;
153
+ }
137
154
  /**
138
155
  * Pairwise dominance comparison (ISO C++ [over.ics.rank]).
139
156
  *
@@ -146,7 +163,7 @@ export function narrowOverloadCandidates(overloads, argCount, argTypes, hookCtx)
146
163
  * Candidates with at least one `Infinity`-ranked slot (incompatible
147
164
  * type) are excluded before pairwise comparison begins.
148
165
  */
149
- function rankByConversion(candidates, argTypes, rankFn) {
166
+ function rankByConversion(candidates, argTypes, rankFn, argTypeClasses) {
150
167
  // Step 1: compute per-slot ranks and exclude non-viable candidates.
151
168
  const viable = [];
152
169
  for (const d of candidates) {
@@ -155,12 +172,17 @@ function rankByConversion(candidates, argTypes, rankFn) {
155
172
  continue;
156
173
  const ranks = [];
157
174
  let ok = true;
158
- for (let i = 0; i < argTypes.length && i < params.length; i++) {
175
+ for (let i = 0; i < argTypes.length; i++) {
176
+ const paramType = parameterTypeAt(params, i);
177
+ if (paramType === undefined) {
178
+ ok = false;
179
+ break;
180
+ }
159
181
  if (argTypes[i] === '') {
160
182
  ranks.push(0); // unknown arg → any-match (rank 0)
161
183
  continue;
162
184
  }
163
- const r = rankFn(argTypes[i], params[i]);
185
+ const r = rankFn(argTypes[i], paramType, argTypeClasses?.[i], parameterTypeClassAt(d.parameterTypeClasses, i));
164
186
  if (!isFinite(r)) {
165
187
  ok = false;
166
188
  break;
@@ -190,6 +212,18 @@ function rankByConversion(candidates, argTypes, rankFn) {
190
212
  }
191
213
  return viable.filter((_, idx) => !dominated.has(idx)).map((v) => v.def);
192
214
  }
215
+ function parameterTypeAt(params, argIndex) {
216
+ if (argIndex < params.length)
217
+ return params[argIndex];
218
+ return params[params.length - 1] === '...' ? '...' : undefined;
219
+ }
220
+ function parameterTypeClassAt(params, argIndex) {
221
+ if (params === undefined)
222
+ return undefined;
223
+ if (argIndex < params.length)
224
+ return params[argIndex];
225
+ return params[params.length - 1]?.base === '...' ? params[params.length - 1] : undefined;
226
+ }
193
227
  /**
194
228
  * Compare two per-slot rank vectors.
195
229
  * Returns -1 if `a` dominates `b` (not worse everywhere, better somewhere),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.21",
3
+ "version": "1.6.6-rc.22",
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",