mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
fix(cpp): rank homogeneous braced init overloads
This commit is contained in:
parent
5e96a99b0d
commit
84abd91a0a
6 changed files with 194 additions and 0 deletions
|
|
@ -22,6 +22,8 @@ import { markCppInlineNamespaceRange } from './inline-namespaces.js';
|
|||
import { extractCppTemplateConstraints } from './constraint-extractor.js';
|
||||
import { captureCppMemberLookupFacts } from './member-lookup.js';
|
||||
|
||||
const CPP_BRACED_INIT_TYPE_PREFIX = 'braced-init:';
|
||||
|
||||
export function emitCppScopeCaptures(
|
||||
sourceText: string,
|
||||
filePath: string,
|
||||
|
|
@ -1022,6 +1024,8 @@ function unknownTypeClass(base: string): ParameterTypeClass {
|
|||
*/
|
||||
function inferCppLiteralType(node: SyntaxNode): string {
|
||||
switch (node.type) {
|
||||
case 'initializer_list':
|
||||
return inferCppBracedInitType(node);
|
||||
case 'number_literal': {
|
||||
const text = node.text;
|
||||
// Floating-point literals contain '.', 'e', 'E', or end with 'f'/'F'
|
||||
|
|
@ -1053,6 +1057,25 @@ function inferCppLiteralType(node: SyntaxNode): string {
|
|||
}
|
||||
}
|
||||
|
||||
function inferCppBracedInitType(node: SyntaxNode): string {
|
||||
const elementTypes: string[] = [];
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i);
|
||||
if (child === null) continue;
|
||||
if (child.type === ',' || child.type === '{' || child.type === '}') continue;
|
||||
const elementType = inferCppLiteralType(child);
|
||||
if (elementType === '' || elementType.startsWith(CPP_BRACED_INIT_TYPE_PREFIX)) {
|
||||
return `${CPP_BRACED_INIT_TYPE_PREFIX}unknown`;
|
||||
}
|
||||
elementTypes.push(elementType);
|
||||
}
|
||||
if (elementTypes.length === 0) return `${CPP_BRACED_INIT_TYPE_PREFIX}unknown`;
|
||||
const first = elementTypes[0];
|
||||
return elementTypes.every((type) => type === first)
|
||||
? `${CPP_BRACED_INIT_TYPE_PREFIX}${first}`
|
||||
: `${CPP_BRACED_INIT_TYPE_PREFIX}unknown`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the declared type of a variable by scanning sibling declarations
|
||||
* in the enclosing compound_statement (function body). Handles:
|
||||
|
|
|
|||
|
|
@ -32,6 +32,22 @@ const INTEGRAL_PROMOTION = new Map([
|
|||
['bool', 'int'],
|
||||
]);
|
||||
|
||||
const BRACED_INIT_TYPE_PREFIX = 'braced-init:';
|
||||
const BRACED_INIT_CONTAINER_TYPES = new Set([
|
||||
'array',
|
||||
'deque',
|
||||
'list',
|
||||
'set',
|
||||
'std::array',
|
||||
'std::deque',
|
||||
'std::list',
|
||||
'std::set',
|
||||
'std::unordered_set',
|
||||
'std::vector',
|
||||
'unordered_set',
|
||||
'vector',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Return the conversion rank from `argType` to `paramType`.
|
||||
*
|
||||
|
|
@ -46,6 +62,11 @@ export function cppConversionRank(
|
|||
argTypeClass?: ParameterTypeClass,
|
||||
paramTypeClass?: ParameterTypeClass,
|
||||
): number {
|
||||
const bracedInitElementType = parseBracedInitElementType(argType);
|
||||
if (bracedInitElementType !== undefined) {
|
||||
if (bracedInitElementType === 'unknown') return Infinity;
|
||||
return bracedInitConversionRank(paramType);
|
||||
}
|
||||
if (argType === paramType) {
|
||||
return exactShapeCompatible(argTypeClass, paramTypeClass) ? 0 : Infinity;
|
||||
}
|
||||
|
|
@ -60,6 +81,32 @@ export function cppConversionRank(
|
|||
return Infinity;
|
||||
}
|
||||
|
||||
function parseBracedInitElementType(argType: string): string | undefined {
|
||||
const elementType = argType.slice(BRACED_INIT_TYPE_PREFIX.length);
|
||||
return argType.startsWith(BRACED_INIT_TYPE_PREFIX) && elementType !== ''
|
||||
? elementType
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function bracedInitConversionRank(paramType: string): number {
|
||||
const targetBase = bracedInitTargetBase(paramType);
|
||||
if (targetBase === 'initializer_list' || targetBase === 'std::initializer_list') return 0;
|
||||
if (BRACED_INIT_CONTAINER_TYPES.has(targetBase)) return 1;
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
function bracedInitTargetBase(paramType: string): string {
|
||||
let type = paramType.trim();
|
||||
type = type.replace(/\b(const|volatile|restrict|mutable|constexpr)\b/g, '').trim();
|
||||
type = type.replace(/[&*]+\s*$/, '').trim();
|
||||
while (type.includes('<')) {
|
||||
const stripped = type.replace(/<[^<>]*>/g, '');
|
||||
if (stripped === type) break;
|
||||
type = stripped;
|
||||
}
|
||||
return type.trim();
|
||||
}
|
||||
|
||||
function isPointer(typeClass: ParameterTypeClass | undefined): boolean {
|
||||
return typeClass?.indirection === 'pointer' && typeClass.pointerDepth > 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ import type {
|
|||
SymbolDefinition,
|
||||
} from 'gitnexus-shared';
|
||||
|
||||
const CONVERSION_ONLY_ARG_TYPE_PREFIXES = ['braced-init:'];
|
||||
|
||||
/**
|
||||
* Per-slot conversion-rank function. Returns a numeric cost for
|
||||
* converting `argType` to `paramType`:
|
||||
|
|
@ -176,6 +178,7 @@ export function narrowOverloadCandidates(
|
|||
hookCtx.argumentTypeClasses,
|
||||
);
|
||||
if (ranked.length > 0) result = ranked;
|
||||
else if (hasConversionOnlyArgType(argTypes)) result = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,6 +225,12 @@ export function narrowOverloadCandidates(
|
|||
return result;
|
||||
}
|
||||
|
||||
function hasConversionOnlyArgType(argTypes: readonly string[]): boolean {
|
||||
return argTypes.some((type) =>
|
||||
CONVERSION_ONLY_ARG_TYPE_PREFIXES.some((prefix) => type.startsWith(prefix)),
|
||||
);
|
||||
}
|
||||
|
||||
function exactTypeSlotMatches(
|
||||
argType: string,
|
||||
paramType: string,
|
||||
|
|
|
|||
41
gitnexus/test/fixtures/lang-resolution/cpp-braced-init-list-overload/service.cpp
vendored
Normal file
41
gitnexus/test/fixtures/lang-resolution/cpp-braced-init-list-overload/service.cpp
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
namespace std {
|
||||
template <typename T>
|
||||
class initializer_list {};
|
||||
|
||||
template <typename T>
|
||||
class vector {};
|
||||
}
|
||||
|
||||
class InitListService {
|
||||
public:
|
||||
void consume(std::initializer_list<int> values) {}
|
||||
void consume(int value) {}
|
||||
|
||||
void consumeVector(std::vector<int> values) {}
|
||||
void consumeVector(int value) {}
|
||||
|
||||
void consumeMixed(std::initializer_list<int> values) {}
|
||||
void consumeMixed(std::initializer_list<double> values) {}
|
||||
|
||||
void consumeEmpty(std::initializer_list<int> values) {}
|
||||
void consumeEmpty(std::initializer_list<double> values) {}
|
||||
|
||||
void callHomogeneousInitList() {
|
||||
consume({1, 2, 3});
|
||||
}
|
||||
|
||||
void callHomogeneousVector() {
|
||||
consumeVector({1, 2, 3});
|
||||
}
|
||||
|
||||
void callHeterogeneousInitList() {
|
||||
consumeMixed({1, 2.0});
|
||||
}
|
||||
|
||||
void callEmptyInitList() {
|
||||
consumeEmpty({});
|
||||
}
|
||||
};
|
||||
|
|
@ -1230,6 +1230,52 @@ describe('C++ overload disambiguation by parameter types', () => {
|
|||
|
||||
// ── Phase P: Same-arity overloads — cross-file + chain resolution ─────────
|
||||
|
||||
describe('C++ braced-init-list overload disambiguation (#1899 A8 conservative)', () => {
|
||||
let result: PipelineResult;
|
||||
|
||||
beforeAll(async () => {
|
||||
result = await runPipelineFromRepo(
|
||||
path.join(FIXTURES, 'cpp-braced-init-list-overload'),
|
||||
() => {},
|
||||
);
|
||||
}, 60000);
|
||||
|
||||
const callsFrom = (source: string, target: string) =>
|
||||
getRelationships(result, 'CALLS').filter(
|
||||
(edge) => edge.source === source && edge.target === target,
|
||||
);
|
||||
|
||||
const singleTargetParameterTypes = (source: string, target: string) => {
|
||||
const calls = callsFrom(source, target);
|
||||
expect(calls).toHaveLength(1);
|
||||
const [call] = calls;
|
||||
expect(call).toBeDefined();
|
||||
return call === undefined
|
||||
? undefined
|
||||
: result.graph.getNode(call.rel.targetId)?.properties.parameterTypes;
|
||||
};
|
||||
|
||||
it('resolves homogeneous literal braces to initializer_list overloads', () => {
|
||||
expect(singleTargetParameterTypes('callHomogeneousInitList', 'consume')).toEqual([
|
||||
'std::initializer_list<int>',
|
||||
]);
|
||||
});
|
||||
|
||||
it('resolves homogeneous literal braces to container overloads', () => {
|
||||
expect(singleTargetParameterTypes('callHomogeneousVector', 'consumeVector')).toEqual([
|
||||
'std::vector<int>',
|
||||
]);
|
||||
});
|
||||
|
||||
it('suppresses heterogeneous braced-init lists instead of guessing an element type', () => {
|
||||
expect(callsFrom('callHeterogeneousInitList', 'consumeMixed')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('suppresses empty braced-init lists instead of guessing an element type', () => {
|
||||
expect(callsFrom('callEmptyInitList', 'consumeEmpty')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('C++ same-arity overload cross-file and chain resolution', () => {
|
||||
let result: PipelineResult;
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,34 @@ describe('cppConversionRank user-defined conversion ranks (#1631)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('cppConversionRank braced-init-list ranks (#1899)', () => {
|
||||
it('ranks homogeneous braced-init lists toward initializer_list and containers', () => {
|
||||
expect(cppConversionRank('braced-init:int', 'std::initializer_list<int>')).toBe(0);
|
||||
expect(cppConversionRank('braced-init:int', 'std::vector<int>')).toBe(1);
|
||||
expect(cppConversionRank('braced-init:int', 'int')).toBe(Infinity);
|
||||
});
|
||||
|
||||
it('suppresses unknown braced-init lists when conversion ranking finds no viable target', () => {
|
||||
const byIntList = mkDef(
|
||||
'f:int-list',
|
||||
['std::initializer_list<int>'],
|
||||
[value('std::initializer_list')],
|
||||
);
|
||||
const byDoubleList = mkDef(
|
||||
'f:double-list',
|
||||
['std::initializer_list<double>'],
|
||||
[value('std::initializer_list')],
|
||||
);
|
||||
|
||||
const result = narrowOverloadCandidates([byIntList, byDoubleList], 1, ['braced-init:unknown'], {
|
||||
argumentTypeClasses: [value('braced-init:unknown')],
|
||||
conversionRankFn: cppConversionRank,
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('narrowOverloadCandidates with C++ pointer-rank sidecars (#1637)', () => {
|
||||
it('selects pointer overload for nullptr over bool overload', () => {
|
||||
const byPointer = mkDef('f:intptr', ['int'], [pointer('int')]);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue