gitnexus 1.6.6-rc.72 → 1.6.6-rc.73

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.
@@ -50,6 +50,10 @@ export interface SymbolDefinition {
50
50
  * `ScopeResolver.constraintCompatibility` hook during overload narrowing.
51
51
  * Absent for symbols that have no constraints (the common case). */
52
52
  templateConstraints?: unknown;
53
+ /** True when the producing language marked this callable as explicit.
54
+ * Currently used by C++ overload ranking to exclude explicit constructors
55
+ * from implicit user-defined conversion candidates. */
56
+ isExplicit?: boolean;
53
57
  /** Links Method/Constructor/Property to owning Class/Struct/Trait nodeId */
54
58
  ownerId?: string;
55
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"symbol-definition.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/symbol-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACjE,4CAA4C;IAC5C,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB;;+FAE2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;iHAC6G;IAC7G,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;uFACmF;IACnF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;4FACwF;IACxF,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;yEAKqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
1
+ {"version":3,"file":"symbol-definition.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/symbol-definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,gBAAgB,GAAG,SAAS,CAAC;IACjE,4CAA4C;IAC5C,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3E,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB;;+FAE2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;iHAC6G;IAC7G,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;uFACmF;IACnF,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;4FACwF;IACxF,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;yEAKqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;4DAEwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -89,6 +89,9 @@ export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
89
89
  if (arity.parameterTypeClasses !== undefined) {
90
90
  grouped['@declaration.parameter-type-classes'] = syntheticCapture('@declaration.parameter-type-classes', fnNode, JSON.stringify(arity.parameterTypeClasses));
91
91
  }
92
+ if (hasExplicitSpecifier(fnNode)) {
93
+ grouped['@declaration.is-explicit'] = syntheticCapture('@declaration.is-explicit', fnNode, 'true');
94
+ }
92
95
  // Detect static storage class (file-local linkage)
93
96
  if (hasStaticStorageClass(fnNode)) {
94
97
  const nameText = grouped['@declaration.name']?.text;
@@ -1433,6 +1436,20 @@ function extractDeclaratorLeafName(node) {
1433
1436
  }
1434
1437
  return null;
1435
1438
  }
1439
+ /**
1440
+ * Check if a C++ declaration has an `explicit` specifier. Tree-sitter-cpp
1441
+ * exposes `explicit` as a direct keyword child on constructor declarations in
1442
+ * current grammar builds; the bounded text prefix keeps this resilient across
1443
+ * small grammar shape differences without scanning whole function bodies.
1444
+ */
1445
+ function hasExplicitSpecifier(node) {
1446
+ for (let i = 0; i < node.childCount; i++) {
1447
+ const child = node.child(i);
1448
+ if (child !== null && child.text === 'explicit')
1449
+ return true;
1450
+ }
1451
+ return /\bexplicit\b/.test(node.text.slice(0, 128));
1452
+ }
1436
1453
  /**
1437
1454
  * Check if a C++ function_definition or declaration has `static` storage class.
1438
1455
  */
@@ -12,7 +12,8 @@
12
12
  * - rank 2: standard conversion (arithmetic, nullptr -> T*, T* -> bool,
13
13
  * T* -> void*)
14
14
  * - rank 3: nullptr -> bool (kept worse than nullptr -> T*)
15
- * - rank 4: ellipsis conversion (worst viable)
15
+ * - rank 4: user-defined conversion (one-step, conservative)
16
+ * - rank 5: ellipsis conversion (worst viable)
16
17
  * - Infinity: mismatch (string -> int, user types, unsupported shapes)
17
18
  *
18
19
  * This function is intentionally C++-specific. Other languages may define
@@ -23,7 +24,8 @@ import type { ParameterTypeClass } from '../../../../_shared/index.js';
23
24
  * Return the conversion rank from `argType` to `paramType`.
24
25
  *
25
26
  * @returns 0 for exact match, 1 for integral promotion, 2 for standard
26
- * conversion, 3 for nullptr -> bool, 4 for ellipsis, Infinity
27
+ * conversion, 3 for nullptr -> bool, 4 for user-defined conversion,
28
+ * 5 for ellipsis, Infinity
27
29
  * for mismatch.
28
30
  */
29
31
  export declare function cppConversionRank(argType: string, paramType: string, argTypeClass?: ParameterTypeClass, paramTypeClass?: ParameterTypeClass): number;
@@ -12,12 +12,14 @@
12
12
  * - rank 2: standard conversion (arithmetic, nullptr -> T*, T* -> bool,
13
13
  * T* -> void*)
14
14
  * - rank 3: nullptr -> bool (kept worse than nullptr -> T*)
15
- * - rank 4: ellipsis conversion (worst viable)
15
+ * - rank 4: user-defined conversion (one-step, conservative)
16
+ * - rank 5: ellipsis conversion (worst viable)
16
17
  * - Infinity: mismatch (string -> int, user types, unsupported shapes)
17
18
  *
18
19
  * This function is intentionally C++-specific. Other languages may define
19
20
  * their own `ConversionRankFn` in the future.
20
21
  */
22
+ import { hasCppUserDefinedConversion } from './user-defined-conversions.js';
21
23
  /** Set of normalized arithmetic types that support implicit conversion. */
22
24
  const ARITHMETIC = new Set(['int', 'double', 'char', 'bool']);
23
25
  /** Integral promotion targets: char -> int and bool -> int are rank 1. */
@@ -29,7 +31,8 @@ const INTEGRAL_PROMOTION = new Map([
29
31
  * Return the conversion rank from `argType` to `paramType`.
30
32
  *
31
33
  * @returns 0 for exact match, 1 for integral promotion, 2 for standard
32
- * conversion, 3 for nullptr -> bool, 4 for ellipsis, Infinity
34
+ * conversion, 3 for nullptr -> bool, 4 for user-defined conversion,
35
+ * 5 for ellipsis, Infinity
33
36
  * for mismatch.
34
37
  */
35
38
  export function cppConversionRank(argType, paramType, argTypeClass, paramTypeClass) {
@@ -37,7 +40,7 @@ export function cppConversionRank(argType, paramType, argTypeClass, paramTypeCla
37
40
  return exactShapeCompatible(argTypeClass, paramTypeClass) ? 0 : Infinity;
38
41
  }
39
42
  if (paramType === '...')
40
- return 4;
43
+ return 5;
41
44
  if (INTEGRAL_PROMOTION.get(argType) === paramType)
42
45
  return 1;
43
46
  if (ARITHMETIC.has(argType) && ARITHMETIC.has(paramType))
@@ -50,6 +53,8 @@ export function cppConversionRank(argType, paramType, argTypeClass, paramTypeCla
50
53
  return 2;
51
54
  if (isPointer(argTypeClass) && isPointer(paramTypeClass) && paramType === 'void')
52
55
  return 2;
56
+ if (hasCppUserDefinedConversion(argType, paramType))
57
+ return 4;
53
58
  return Infinity;
54
59
  }
55
60
  function isPointer(typeClass) {
@@ -224,6 +224,12 @@ const CPP_SCOPE_QUERY = `
224
224
  declarator: (function_declarator
225
225
  declarator: (field_identifier) @declaration.name))) @declaration.method
226
226
 
227
+ ;; Constructor prototype in class body: User(int id);
228
+ (field_declaration_list
229
+ (declaration
230
+ declarator: (function_declarator
231
+ declarator: (identifier) @declaration.name)) @declaration.method)
232
+
227
233
  ;; Method prototype with reference return: User& getRef();
228
234
  (field_declaration
229
235
  declarator: (reference_declarator
@@ -14,6 +14,7 @@ import { populateCppAssociatedNamespaces, clearCppAdlState, pickCppAdlCandidates
14
14
  import { clearCppInlineNamespaces, populateCppInlineNamespaceScopes, resolveCppQualifiedNamespaceMember, } from './inline-namespaces.js';
15
15
  import { populateCppRangeBindings } from './range-bindings.js';
16
16
  import { cppConstraintCompatibility } from './constraint-filter.js';
17
+ import { clearCppUserDefinedConversions, populateCppUserDefinedConversions, } from './user-defined-conversions.js';
17
18
  /**
18
19
  * C++ `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
19
20
  * the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
@@ -39,6 +40,7 @@ export const cppScopeResolver = {
39
40
  clearCppDependentBases();
40
41
  clearCppAdlState();
41
42
  clearCppInlineNamespaces();
43
+ clearCppUserDefinedConversions();
42
44
  return scanCppHeaderFiles(repoPath);
43
45
  },
44
46
  resolveImportTarget: (targetRaw, fromFile, allFilePaths, resolutionConfig) => {
@@ -80,6 +82,10 @@ export const cppScopeResolver = {
80
82
  // by ADL (U2 of plan 2026-05-13-001) to identify each argument type's
81
83
  // associated namespace for Koenig lookup.
82
84
  populateCppAssociatedNamespaces(parsed);
85
+ // Build conservative one-step user-defined conversion facts for
86
+ // overload ranking (#1631): implicit converting constructors only,
87
+ // with no chaining or conversion-operator handling.
88
+ populateCppUserDefinedConversions(parsed);
83
89
  },
84
90
  // Resolve recorded template-class → dependent-base simple names to
85
91
  // class nodeIds for two-phase template lookup (U3 of plan
@@ -0,0 +1,5 @@
1
+ import type { ParsedFile } from '../../../../_shared/index.js';
2
+ export declare function clearCppUserDefinedConversions(): void;
3
+ export declare function hasCppUserDefinedConversion(argType: string, paramType: string): boolean;
4
+ export declare function populateCppUserDefinedConversions(parsed: ParsedFile): void;
5
+ export declare function registerCppUserDefinedConversion(argType: string, paramType: string): void;
@@ -0,0 +1,122 @@
1
+ import { normalizeCppParamType } from './arity-metadata.js';
2
+ const userDefinedConversions = new Set();
3
+ const pendingUserDefinedConversions = [];
4
+ const classIdentitiesBySimpleName = new Map();
5
+ export function clearCppUserDefinedConversions() {
6
+ userDefinedConversions.clear();
7
+ pendingUserDefinedConversions.length = 0;
8
+ classIdentitiesBySimpleName.clear();
9
+ }
10
+ export function hasCppUserDefinedConversion(argType, paramType) {
11
+ return userDefinedConversions.has(conversionKey(argType, paramType));
12
+ }
13
+ export function populateCppUserDefinedConversions(parsed) {
14
+ const scopesById = new Map();
15
+ for (const scope of parsed.scopes)
16
+ scopesById.set(scope.id, scope);
17
+ for (const classScope of parsed.scopes) {
18
+ if (classScope.kind !== 'Class')
19
+ continue;
20
+ const classDef = classScope.ownedDefs.find(isClassLike);
21
+ if (classDef !== undefined)
22
+ recordClassIdentity(classDef);
23
+ }
24
+ for (const classScope of parsed.scopes) {
25
+ if (classScope.kind !== 'Class')
26
+ continue;
27
+ const classDef = classScope.ownedDefs.find(isClassLike);
28
+ if (classDef === undefined)
29
+ continue;
30
+ const className = normalizedSimpleName(classDef);
31
+ if (className === '')
32
+ continue;
33
+ const methodDefs = collectClassMethodDefs(classScope.id, parsed, scopesById);
34
+ for (const def of methodDefs) {
35
+ const simpleName = simpleNameOf(def);
36
+ if (simpleName === className && def.parameterTypes?.length === 1) {
37
+ if (def.isExplicit === true)
38
+ continue;
39
+ registerPendingCppUserDefinedConversion(def.parameterTypes[0], className, className);
40
+ }
41
+ }
42
+ }
43
+ rebuildCppUserDefinedConversions();
44
+ }
45
+ export function registerCppUserDefinedConversion(argType, paramType) {
46
+ if (argType === '' || paramType === '')
47
+ return;
48
+ if (argType === paramType)
49
+ return;
50
+ userDefinedConversions.add(conversionKey(argType, paramType));
51
+ }
52
+ function collectClassMethodDefs(classScopeId, parsed, scopesById) {
53
+ const methods = [];
54
+ const classScope = scopesById.get(classScopeId);
55
+ if (classScope === undefined)
56
+ return methods;
57
+ for (const def of classScope.ownedDefs) {
58
+ if (isCallableMember(def))
59
+ methods.push(def);
60
+ }
61
+ for (const scope of parsed.scopes) {
62
+ if (scope.parent !== classScopeId)
63
+ continue;
64
+ if (scope.kind === 'Class')
65
+ continue;
66
+ for (const def of scope.ownedDefs) {
67
+ if (isCallableMember(def))
68
+ methods.push(def);
69
+ }
70
+ }
71
+ return methods;
72
+ }
73
+ function conversionKey(argType, paramType) {
74
+ return `${argType}\0${paramType}`;
75
+ }
76
+ function registerPendingCppUserDefinedConversion(argType, paramType, ownerClassName) {
77
+ if (argType === '' || paramType === '')
78
+ return;
79
+ if (argType === paramType)
80
+ return;
81
+ pendingUserDefinedConversions.push({ argType, paramType, ownerClassName });
82
+ }
83
+ function rebuildCppUserDefinedConversions() {
84
+ userDefinedConversions.clear();
85
+ for (const conversion of pendingUserDefinedConversions) {
86
+ if (isAmbiguousClassName(conversion.ownerClassName))
87
+ continue;
88
+ userDefinedConversions.add(conversionKey(conversion.argType, conversion.paramType));
89
+ }
90
+ }
91
+ function recordClassIdentity(def) {
92
+ const simpleName = normalizedSimpleName(def);
93
+ if (simpleName === '')
94
+ return;
95
+ const identities = classIdentitiesBySimpleName.get(simpleName) ?? new Set();
96
+ identities.add(normalizedQualifiedClassName(def));
97
+ classIdentitiesBySimpleName.set(simpleName, identities);
98
+ }
99
+ function isAmbiguousClassName(simpleName) {
100
+ return (classIdentitiesBySimpleName.get(simpleName)?.size ?? 0) > 1;
101
+ }
102
+ function normalizedQualifiedClassName(def) {
103
+ const qualifiedName = def.qualifiedName ?? simpleNameOf(def);
104
+ if (qualifiedName === '' || !qualifiedName.includes('.'))
105
+ return `${def.filePath}:${def.nodeId}`;
106
+ return qualifiedName
107
+ .split('.')
108
+ .map((part) => normalizeCppParamType(part))
109
+ .join('.');
110
+ }
111
+ function normalizedSimpleName(def) {
112
+ return normalizeCppParamType(simpleNameOf(def));
113
+ }
114
+ function simpleNameOf(def) {
115
+ return def.qualifiedName?.split('.').pop() ?? def.qualifiedName ?? '';
116
+ }
117
+ function isClassLike(def) {
118
+ return def.type === 'Class' || def.type === 'Struct' || def.type === 'Interface';
119
+ }
120
+ function isCallableMember(def) {
121
+ return def.type === 'Method' || def.type === 'Constructor';
122
+ }
@@ -398,6 +398,7 @@ function buildDefFromDeclarationMatch(match, anchor, filePath) {
398
398
  const declaredType = match['@declaration.field-type']?.text;
399
399
  const returnType = match['@declaration.return-type']?.text;
400
400
  const templateConstraints = parseJsonCapture(match['@declaration.template-constraints']);
401
+ const isExplicit = parseBooleanCapture(match['@declaration.is-explicit']);
401
402
  return {
402
403
  nodeId: makeDefId(filePath, anchor.range, type, nameCap.text),
403
404
  filePath,
@@ -411,6 +412,7 @@ function buildDefFromDeclarationMatch(match, anchor, filePath) {
411
412
  ...(returnType !== undefined ? { returnType } : {}),
412
413
  ...(templateArguments !== undefined ? { templateArguments } : {}),
413
414
  ...(templateConstraints !== undefined ? { templateConstraints } : {}),
415
+ ...(isExplicit === true ? { isExplicit: true } : {}),
414
416
  };
415
417
  }
416
418
  /** Parse an opaque JSON payload synthesized by per-language captures
@@ -433,6 +435,15 @@ function parseIntCapture(cap) {
433
435
  const n = Number.parseInt(cap.text, 10);
434
436
  return Number.isFinite(n) ? n : undefined;
435
437
  }
438
+ function parseBooleanCapture(cap) {
439
+ if (cap === undefined)
440
+ return undefined;
441
+ if (cap.text === 'true')
442
+ return true;
443
+ if (cap.text === 'false')
444
+ return false;
445
+ return undefined;
446
+ }
436
447
  function parseJsonParameterTypeClassesCapture(cap) {
437
448
  if (cap === undefined)
438
449
  return undefined;
@@ -843,6 +854,7 @@ const KNOWN_SUB_TAGS = new Set([
843
854
  '@declaration.parameter-types',
844
855
  '@declaration.parameter-type-classes',
845
856
  '@declaration.template-constraints',
857
+ '@declaration.is-explicit',
846
858
  ]);
847
859
  /**
848
860
  * Return the anchor capture for a match — the one whose name begins with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.6-rc.72",
3
+ "version": "1.6.6-rc.73",
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",