feat(kernel): preserve union nodes across extraction

This commit is contained in:
ctype_lab
2026-08-06 17:11:26 +09:00
parent 6978acc92e
commit 8e3cde6994
9 changed files with 59 additions and 45 deletions
+1 -1
View File
@@ -559,7 +559,7 @@ export class ContextBuilder {
// but are almost never what exploration queries want.
const searchKinds = opts.nodeKinds && opts.nodeKinds.length > 0
? opts.nodeKinds
: ['file', 'module', 'class', 'struct', 'interface', 'trait', 'protocol',
: ['file', 'module', 'class', 'struct', 'union', 'interface', 'trait', 'protocol',
'function', 'method', 'property', 'field', 'variable', 'constant',
'enum', 'enum_member', 'type_alias', 'namespace', 'export',
'route', 'component'] as NodeKind[];
+2 -2
View File
@@ -343,7 +343,7 @@ export function getExploreOutputBudget(fileCount: number): ExploreOutputBudget {
*/
export const RELEVANCE_KIND_WEIGHT: Readonly<Record<string, number>> = {
// Callables and types: the answer lives in one of these.
function: 1, method: 1, class: 1, struct: 1, interface: 1, trait: 1,
function: 1, method: 1, class: 1, struct: 1, union: 1, interface: 1, trait: 1,
protocol: 1, component: 1, route: 1, enum: 1, type_alias: 1, constructor: 1,
// Containers: real structure, but a whole namespace/module matching a term is
// a coarser signal than a callable matching it.
@@ -3968,7 +3968,7 @@ export class ToolHandler {
const superMany = new Map<string, boolean>();
const definesPolymorphicSupertype = (nodes: Node[]): boolean => {
for (const n of nodes) {
if (n.kind !== 'class' && n.kind !== 'interface' && n.kind !== 'struct'
if (n.kind !== 'class' && n.kind !== 'interface' && n.kind !== 'struct' && n.kind !== 'union'
&& n.kind !== 'trait' && n.kind !== 'protocol' && n.kind !== 'type_alias') continue;
let many = superMany.get(n.id);
if (many === undefined) {
+2 -2
View File
@@ -703,7 +703,7 @@ export async function cFnPointerDispatchEdges(
if (prof) { prof.nodesMs += Date.now() - tN; prof.nodesN++; }
const structs: CfnptrFileIn['structs'] = [];
for (const st of fileNodes) {
if (st.kind !== 'struct') continue;
if (st.kind !== 'struct' && st.kind !== 'union') continue;
// sliceLinesPre semantics ride along: falsy startLine never parses,
// and `endLine ?? startLine` is applied here so the kernel sees the
// exact slice bounds the JS sweep would use.
@@ -740,7 +740,7 @@ export async function cFnPointerDispatchEdges(
if (prof) { prof.nodesMs += Date.now() - tN; prof.nodesN++; }
let lines: string[] | null = null;
for (const st of fileNodes) {
if (st.kind !== 'struct') continue;
if (st.kind !== 'struct' && st.kind !== 'union') continue;
lines ??= s.split('\n');
const body = sliceLinesPre(lines, st.startLine, st.endLine);
const open = body.indexOf('{');
+1
View File
@@ -1827,6 +1827,7 @@ function resolveRustPathReference(
n.name === leaf &&
(n.kind === 'function' ||
n.kind === 'struct' ||
n.kind === 'union' ||
n.kind === 'enum' ||
n.kind === 'trait' ||
n.kind === 'type_alias' ||
+4 -4
View File
@@ -798,12 +798,12 @@ function lookupCalleeReturnType(
return candidates.find((n) => n.kind === 'function')?.returnType ?? null;
}
/** Does the graph contain a class/struct named `name`'s last segment? */
/** Does the graph contain an aggregate type named `name`'s last segment? */
function cppClassExists(name: string, ref: UnresolvedRef, context: ResolutionContext): boolean {
const last = cppLastSegment(name);
return context
.getNodesByName(last)
.some((n) => (n.kind === 'class' || n.kind === 'struct') && n.language === ref.language);
.some((n) => (n.kind === 'class' || n.kind === 'struct' || n.kind === 'union') && n.language === ref.language);
}
/**
@@ -1771,7 +1771,7 @@ export function matchMethodCall(
);
for (const classNode of classCandidates) {
if (classNode.kind === 'class' || classNode.kind === 'struct' || classNode.kind === 'interface') {
if (classNode.kind === 'class' || classNode.kind === 'struct' || classNode.kind === 'union' || classNode.kind === 'interface') {
// Skip cross-language class matches
if (classNode.language !== ref.language) continue;
@@ -1807,7 +1807,7 @@ export function matchMethodCall(
ref.filePath,
);
for (const classNode of fuzzyClassCandidates) {
if (classNode.kind === 'class' || classNode.kind === 'struct' || classNode.kind === 'interface') {
if (classNode.kind === 'class' || classNode.kind === 'struct' || classNode.kind === 'union' || classNode.kind === 'interface') {
// Skip cross-language class matches
if (classNode.language !== ref.language) continue;
+1
View File
@@ -393,6 +393,7 @@ export function kindBonus(kind: Node['kind']): number {
interface: 9,
type_alias: 6,
struct: 6,
union: 6,
trait: 9,
enum: 5,
component: 8,