fix(union): complete downstream container handling

This commit is contained in:
ctype_lab
2026-08-06 17:50:25 +09:00
parent e922563e05
commit e2195940fb
10 changed files with 136 additions and 32 deletions
+39
View File
@@ -86,6 +86,45 @@ int dispatch(struct ops o) { return o.handler(); }
expect(edges.every((e) => e.via === 'ops.handler')).toBe(true);
});
it('bridges function-pointer fields declared in a union', async () => {
write('union-ops.c', `
union ops { int (*handler)(void); };
static int on_open(void) { return 1; }
static union ops the_ops = { .handler = on_open };
int dispatch(union ops o) { return o.handler(); }
`);
const edges = await load();
expect(has(edges, 'dispatch', 'on_open')).toBe(true);
expect(edges.every((e) => e.via === 'ops.handler')).toBe(true);
});
it('bridges an inline union table whose entries are macro-built', async () => {
write('inline-union.c', `
#define SLOT(fn) { fn }
static int on_open(void) { return 1; }
static union inline_ops { int (*handler)(void); } ops[] = { SLOT(on_open) };
int dispatch(union inline_ops o) { return o.handler(); }
`);
const edges = await load();
expect(has(edges, 'dispatch', 'on_open')).toBe(true);
});
it('bridges a union table declared through an object-macro type alias', async () => {
write('alias-union.c', `
#define OPS_TYPE union ops
#define SLOT(fn) { fn }
union ops { int (*handler)(void); };
static int on_open(void) { return 1; }
static OPS_TYPE ops[] = { SLOT(on_open) };
int dispatch(union ops o) { return o.handler(); }
`);
const edges = await load();
expect(has(edges, 'dispatch', 'on_open')).toBe(true);
});
it('bridges the typedef-field + field←field double-hop (the hook_demo.c shape)', async () => {
write('hook.c', `
typedef void (*hook_func)(void);
+16 -1
View File
@@ -135,10 +135,16 @@ export function validateEmail(email: string): boolean {
`
);
fs.writeFileSync(
path.join(srcDir, 'callback_ops.c'),
`union CallbackOps { int (*run)(int); };
`
);
// Initialize CodeGraph
cg = CodeGraph.initSync(testDir, {
config: {
include: ['**/*.ts'],
include: ['**/*.ts', '**/*.c'],
exclude: [],
},
});
@@ -194,6 +200,15 @@ export function validateEmail(email: string): boolean {
).toBe(true);
});
it('includes union definitions in the default context search', async () => {
const result = await cg.findRelevantContext('CallbackOps');
const union = [...result.nodes.values()].find(
(node) => node.kind === 'union' && node.name === 'CallbackOps'
);
expect(union).toBeDefined();
});
it('should include edges in the result', async () => {
const result = await cg.findRelevantContext('checkout', {
traversalDepth: 2,
+28
View File
@@ -1045,6 +1045,34 @@ void initialize() { Packet(); }
expect(outgoing.some((e) => e.kind === 'calls' && e.target === packet!.id)).toBe(false);
});
it('resolves a static call through an imported C++ union to its member', async () => {
fs.writeFileSync(
path.join(tempDir, 'ops.hpp'),
`union Ops {
static int run() { return 1; }
};
`
);
fs.writeFileSync(
path.join(tempDir, 'main.cpp'),
`#include "ops.hpp"
int invoke() { return Ops::run(); }
`
);
cg = await CodeGraph.init(tempDir, { index: true });
cg.resolveReferences();
const invoke = cg.getNodesByKind('function').find((n) => n.name === 'invoke');
const run = cg.getNodesByKind('method').find((n) => n.name === 'run');
expect(invoke).toBeDefined();
expect(run).toBeDefined();
const outgoing = cg.getOutgoingEdges(invoke!.id);
expect(outgoing.some((e) => e.kind === 'calls' && e.target === run!.id)).toBe(true);
});
it('records instantiates for C++ stack/brace construction, targeting the class (#1035)', async () => {
// `Calculator calc(0)` (direct-init) and `Widget w{1, 2}` (brace-init)
// carry the constructor args directly on the declarator — there's no