mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-04 02:31:36 +00:00
fix(cpp): preserve helper calls in pack mixins
This commit is contained in:
parent
463f0701fc
commit
92d9e4bdc7
5 changed files with 65 additions and 66 deletions
|
|
@ -332,13 +332,6 @@ export function emitCppScopeCaptures(
|
|||
'call_expression',
|
||||
);
|
||||
if (freeCallNode !== null) {
|
||||
const callName = grouped['@reference.name']?.text;
|
||||
if (
|
||||
callName !== undefined &&
|
||||
isUnqualifiedCallSuppressedByPackBase(freeCallNode, callName)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const adlAnchorRange = grouped['@reference.call.free']!.range;
|
||||
if (isParenthesizedFunctionCall(freeCallNode)) {
|
||||
markCppAdlSiteNoAdl(filePath, adlAnchorRange.startLine, adlAnchorRange.startCol);
|
||||
|
|
@ -432,59 +425,6 @@ export function emitCppScopeCaptures(
|
|||
return out;
|
||||
}
|
||||
|
||||
function isUnqualifiedCallSuppressedByPackBase(callNode: SyntaxNode, callName: string): boolean {
|
||||
const classNode = findEnclosingClassLike(callNode);
|
||||
if (classNode === null) return false;
|
||||
const baseClause = findChildOfType(classNode, ['base_class_clause']);
|
||||
if (baseClause === null) return false;
|
||||
const hasPackBase = [...iterBaseClasses(baseClause)].some((base) => base.isPackExpansion);
|
||||
if (!hasPackBase) return false;
|
||||
return !classDeclaresMemberNamed(classNode, callName);
|
||||
}
|
||||
|
||||
function findEnclosingClassLike(node: SyntaxNode): SyntaxNode | null {
|
||||
let cur = node.parent;
|
||||
while (cur !== null) {
|
||||
if (cur.type === 'class_specifier' || cur.type === 'struct_specifier') return cur;
|
||||
cur = cur.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function classDeclaresMemberNamed(classNode: SyntaxNode, memberName: string): boolean {
|
||||
const body = findChildOfType(classNode, ['field_declaration_list']);
|
||||
if (body === null) return false;
|
||||
const stack: SyntaxNode[] = [body];
|
||||
while (stack.length > 0) {
|
||||
const node = stack.pop()!;
|
||||
if (
|
||||
(node.type === 'function_declarator' || node.type === 'field_declaration') &&
|
||||
getFunctionDeclaratorName(node) === memberName
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child !== null) stack.push(child);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFunctionDeclaratorName(node: SyntaxNode): string {
|
||||
const nameNode = node.childForFieldName('declarator') ?? node.childForFieldName('name');
|
||||
if (nameNode?.type === 'field_identifier' || nameNode?.type === 'identifier') {
|
||||
return nameNode.text;
|
||||
}
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child === null) continue;
|
||||
const nested = getFunctionDeclaratorName(child);
|
||||
if (nested !== '') return nested;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function extractCppDeclarationReturnType(fnNode: SyntaxNode): string | undefined {
|
||||
const typeNode = fnNode.childForFieldName('type');
|
||||
if (typeNode === null) return undefined;
|
||||
|
|
@ -528,8 +468,7 @@ function emitCppInheritanceCaptures(root: SyntaxNode, out: CaptureMatch[], fileP
|
|||
if (baseClause !== null) {
|
||||
for (const base of iterBaseClasses(baseClause)) {
|
||||
if (base.isPackExpansion) {
|
||||
const className = getTypeIdentifierName(node);
|
||||
if (className !== '') markCppDependentPackBase(filePath, className);
|
||||
markClassWithPackExpandedBase(filePath, node);
|
||||
continue;
|
||||
}
|
||||
const baseName = extractBaseLookupName(base.node);
|
||||
|
|
@ -579,7 +518,7 @@ function detectCppDependentBases(root: SyntaxNode, filePath: string): void {
|
|||
for (const base of iterBaseClasses(baseClause)) {
|
||||
if (base.isPackExpansion || isBaseDependent(base.node, params)) {
|
||||
if (base.isPackExpansion) {
|
||||
markCppDependentPackBase(filePath, className);
|
||||
markClassWithPackExpandedBase(filePath, classNode);
|
||||
}
|
||||
const baseName = extractBaseLookupName(base.node);
|
||||
const baseQualifier = extractBaseLookupQualifier(base.node);
|
||||
|
|
@ -631,6 +570,11 @@ function collectTemplateParameterNames(templateDecl: SyntaxNode): Set<string> {
|
|||
return names;
|
||||
}
|
||||
|
||||
function markClassWithPackExpandedBase(filePath: string, classNode: SyntaxNode): void {
|
||||
const className = getTypeIdentifierName(classNode);
|
||||
if (className !== '') markCppDependentPackBase(filePath, className);
|
||||
}
|
||||
|
||||
interface CppBaseClassEntry {
|
||||
readonly node: SyntaxNode;
|
||||
readonly isPackExpansion: boolean;
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ export function clearCppDependentBases(): void {
|
|||
* found (conservative: avoids false associations).
|
||||
*/
|
||||
export function populateCppDependentBases(parsedFiles: readonly ParsedFile[]): void {
|
||||
if (dependentBasesByFile.size === 0) return;
|
||||
if (dependentBasesByFile.size === 0 && dependentPackBaseClassesByFile.size === 0) return;
|
||||
|
||||
// Build workspace-wide index: simpleName → {nodeId, nsPrefix}[]
|
||||
// nsPrefix is the dot-joined namespace path (qualifiedName without the
|
||||
|
|
@ -303,10 +303,25 @@ export function isCppDependentBaseMember(
|
|||
if (bases === undefined) return false;
|
||||
if (bases.has('*pack-expansion*')) {
|
||||
if (candidateDef.ownerId !== undefined) return candidateDef.ownerId !== enclosing.nodeId;
|
||||
const ownerName = candidateDef.qualifiedName?.split('.').slice(-2, -1)[0];
|
||||
const enclosingName = enclosing.qualifiedName?.split('.').pop();
|
||||
if (candidateDef.type !== 'Method' && candidateDef.type !== 'Constructor') return false;
|
||||
const ownerName = getQualifiedParentName(candidateDef.qualifiedName);
|
||||
const enclosingName = getQualifiedSimpleName(enclosing.qualifiedName);
|
||||
return ownerName !== undefined && ownerName !== enclosingName;
|
||||
}
|
||||
if (candidateDef.ownerId === undefined) return false;
|
||||
return bases.has(candidateDef.ownerId);
|
||||
}
|
||||
|
||||
function getQualifiedParentName(qualifiedName: string | undefined): string | undefined {
|
||||
if (qualifiedName === undefined) return undefined;
|
||||
const lastDot = qualifiedName.lastIndexOf('.');
|
||||
if (lastDot < 0) return undefined;
|
||||
const parent = qualifiedName.slice(0, lastDot);
|
||||
return getQualifiedSimpleName(parent);
|
||||
}
|
||||
|
||||
function getQualifiedSimpleName(qualifiedName: string | undefined): string | undefined {
|
||||
if (qualifiedName === undefined) return undefined;
|
||||
const lastDot = qualifiedName.lastIndexOf('.');
|
||||
return lastDot >= 0 ? qualifiedName.slice(lastDot + 1) : qualifiedName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,11 +116,13 @@ export function emitFreeCallFallback(
|
|||
// enclosing class. When the workspace has multiple methods of
|
||||
// the same name in a single class, choose the best match by
|
||||
// arity + argument types.
|
||||
let fnDefFromImplicitThis = false;
|
||||
if (fnDef === undefined) {
|
||||
fnDef = pickImplicitThisOverload(site, scopes, workspaceIndex, model, {
|
||||
conversionRankFn: options.conversionRankFn,
|
||||
constraintCompatibility: options.constraintCompatibility,
|
||||
});
|
||||
fnDefFromImplicitThis = fnDef !== undefined;
|
||||
}
|
||||
// Scope-chain callable lookup. First-match preserves scope-chain
|
||||
// precedence (local shadows import). When a conversion-rank function
|
||||
|
|
@ -336,6 +338,19 @@ export function emitFreeCallFallback(
|
|||
);
|
||||
}
|
||||
if (fnDef === undefined) continue;
|
||||
if (
|
||||
(fnDefFromImplicitThis || fnDef.type === 'Method' || fnDef.type === 'Constructor') &&
|
||||
options.isCallableVisibleFromCaller !== undefined &&
|
||||
!options.isCallableVisibleFromCaller({
|
||||
callerParsed: parsed,
|
||||
candidate: fnDef,
|
||||
callerScope: site.inScope,
|
||||
scopes,
|
||||
})
|
||||
) {
|
||||
handledSites.add(siteKey(parsed.filePath, site));
|
||||
continue;
|
||||
}
|
||||
const callerGraphId = resolveCallerGraphId(site.inScope, scopes, nodeLookup);
|
||||
if (callerGraphId === undefined) continue;
|
||||
const tgtGraphId = resolveDefGraphId(fnDef.filePath, fnDef, nodeLookup);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ struct B {
|
|||
void sink(int value);
|
||||
void ambiguous(int value);
|
||||
void ambiguous(double value);
|
||||
void helper();
|
||||
|
||||
namespace tools {
|
||||
void namespaceHelper();
|
||||
}
|
||||
|
||||
using tools::namespaceHelper;
|
||||
|
||||
template <class... Ts>
|
||||
void logMany(int, Ts... xs) {
|
||||
|
|
@ -20,6 +27,8 @@ template <class... B>
|
|||
struct Mix : B... {
|
||||
void run() {
|
||||
inherited();
|
||||
helper();
|
||||
namespaceHelper();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -400,6 +400,22 @@ describe('C++ variadic packs and dependent-name resolution (#1894)', () => {
|
|||
expect(calls).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('preserves free helper calls inside a class with a pack-expanded dependent base', () => {
|
||||
const calls = getRelationships(result, 'CALLS').filter(
|
||||
(c) => c.source === 'run' && c.target === 'helper',
|
||||
);
|
||||
|
||||
expect(calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('preserves using-declaration namespace helper calls inside a pack-base class', () => {
|
||||
const calls = getRelationships(result, 'CALLS').filter(
|
||||
(c) => c.source === 'run' && c.target === 'namespaceHelper',
|
||||
);
|
||||
|
||||
expect(calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('resolves current-instantiation unqualified member calls', () => {
|
||||
const calls = getRelationships(result, 'CALLS').filter(
|
||||
(c) => c.source === 'run' && c.target === 'own',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue