diff --git a/src/domain/graph/builder/stages/native-orchestrator.ts b/src/domain/graph/builder/stages/native-orchestrator.ts index fbc86068..a1bf7aa9 100644 --- a/src/domain/graph/builder/stages/native-orchestrator.ts +++ b/src/domain/graph/builder/stages/native-orchestrator.ts @@ -959,14 +959,6 @@ interface PostPassTimings { techniqueBackfillMs: number; } -interface PostPassTimings { - gapDetectMs: number; - chaMs: number; - thisDispatchMs: number; - reclassifyMs: number; - techniqueBackfillMs: number; -} - /** Format timing result from native orchestrator phases + JS post-processing. */ function formatNativeTimingResult( p: Record, diff --git a/src/extractors/cpp.ts b/src/extractors/cpp.ts index b014d58a..e9065e5b 100644 --- a/src/extractors/cpp.ts +++ b/src/extractors/cpp.ts @@ -50,6 +50,9 @@ function walkCppNode(node: TreeSitterNode, ctx: ExtractorOutput): void { case 'call_expression': handleCppCallExpression(node, ctx); break; + case 'declaration': + handleCppDeclaration(node, ctx); + break; } for (let i = 0; i < node.childCount; i++) { @@ -204,6 +207,40 @@ function handleCppInclude(node: TreeSitterNode, ctx: ExtractorOutput): void { }); } +/** + * Seed typeMap for declaration-typed locals: `UserService svc;` and + * `UserService svc = makeService();` both yield typeMap["svc"] = "UserService" + * at confidence 0.9. Mirrors `match_c_family_type_map` ("declaration" branch) + * in the native Rust C++ extractor. + */ +function handleCppDeclaration(node: TreeSitterNode, ctx: ExtractorOutput): void { + const typeNode = node.childForFieldName('type'); + if (!typeNode) return; + const typeName = typeNode.text; + // Skip primitive types — they are never class/struct receivers + if (isPrimitiveCppType(typeName)) return; + for (let i = 0; i < node.childCount; i++) { + const child = node.child(i); + if (!child) continue; + const kind = child.type; + let nameNode: TreeSitterNode | null = null; + if (kind === 'init_declarator') { + nameNode = child.childForFieldName('declarator') ?? null; + } else if (kind === 'identifier') { + nameNode = child; + } + // Note: pointer_declarator / reference_declarator children (e.g. `UserService *svc;`) + // are intentionally skipped here — they are also skipped by the native Rust + // match_c_family_type_map helper, which only handles 'init_declarator' and + // 'identifier' children. Both engines have the same scope for this case. + if (!nameNode) continue; + const varName = unwrapCppDeclaratorName(nameNode); + if (varName) { + ctx.typeMap.set(varName, { type: typeName, confidence: 0.9 }); + } + } +} + function handleCppCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void { const funcNode = node.childForFieldName('function'); if (!funcNode) return; @@ -324,6 +361,43 @@ function extractCppClassFields(classNode: TreeSitterNode): SubDeclaration[] { return fields; } +/** + * Primitive C/C++ types that are never class/struct receivers. Seeding these + * into typeMap would cause spurious receiver edges (e.g. `int x` → `int`). + */ +const CPP_PRIMITIVE_TYPES = new Set([ + 'int', + 'long', + 'short', + 'unsigned', + 'signed', + 'float', + 'double', + 'char', + 'bool', + 'void', + 'wchar_t', + 'auto', + 'size_t', + 'uint8_t', + 'uint16_t', + 'uint32_t', + 'uint64_t', + 'int8_t', + 'int16_t', + 'int32_t', + 'int64_t', + 'ptrdiff_t', + 'intptr_t', + 'uintptr_t', +]); + +function isPrimitiveCppType(typeName: string): boolean { + // Strip qualifiers like `const`, `volatile`, `unsigned` etc. + const base = typeName.split(/\s+/).pop() ?? typeName; + return CPP_PRIMITIVE_TYPES.has(base) || CPP_PRIMITIVE_TYPES.has(typeName); +} + function extractCppEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] { const entries: SubDeclaration[] = []; const body = findChild(enumNode, 'enumerator_list'); diff --git a/src/extractors/cuda.ts b/src/extractors/cuda.ts index 14f30609..f98726a7 100644 --- a/src/extractors/cuda.ts +++ b/src/extractors/cuda.ts @@ -63,6 +63,9 @@ function walkCudaNode(node: TreeSitterNode, ctx: ExtractorOutput): void { case 'call_expression': handleCudaCallExpression(node, ctx); break; + case 'declaration': + handleCudaDeclaration(node, ctx); + break; } for (let i = 0; i < node.childCount; i++) { @@ -204,6 +207,40 @@ function handleCudaInclude(node: TreeSitterNode, ctx: ExtractorOutput): void { }); } +/** + * Seed typeMap for declaration-typed locals: `UserService svc;` and + * `UserService svc = make();` both yield typeMap["svc"] = "UserService" + * at confidence 0.9. Mirrors `match_c_family_type_map` ("declaration" branch) + * in the native Rust CUDA extractor. + */ +function handleCudaDeclaration(node: TreeSitterNode, ctx: ExtractorOutput): void { + const typeNode = node.childForFieldName('type'); + if (!typeNode) return; + const typeName = typeNode.text; + // Skip primitive types — they are never class/struct receivers + if (isCudaPrimitiveType(typeName)) return; + for (let i = 0; i < node.childCount; i++) { + const child = node.child(i); + if (!child) continue; + const kind = child.type; + let nameNode: TreeSitterNode | null = null; + if (kind === 'init_declarator') { + nameNode = child.childForFieldName('declarator') ?? null; + } else if (kind === 'identifier') { + nameNode = child; + } + // Note: pointer_declarator / reference_declarator children (e.g. `UserService *svc;`) + // are intentionally skipped here — they are also skipped by the native Rust + // match_c_family_type_map helper, which only handles 'init_declarator' and + // 'identifier' children. Both engines have the same scope for this case. + if (!nameNode) continue; + const varName = extractCudaFieldName(nameNode); + if (varName) { + ctx.typeMap.set(varName, { type: typeName, confidence: 0.9 }); + } + } +} + function handleCudaCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void { const funcNode = node.childForFieldName('function'); if (!funcNode) return; @@ -374,6 +411,42 @@ function innerCudaDeclarator(node: TreeSitterNode): TreeSitterNode | null { return null; } +/** + * Primitive C/C++/CUDA types that are never class/struct receivers. Seeding + * these into typeMap would produce spurious receiver edges (e.g. `int x` → `int`). + */ +const CUDA_PRIMITIVE_TYPES = new Set([ + 'int', + 'long', + 'short', + 'unsigned', + 'signed', + 'float', + 'double', + 'char', + 'bool', + 'void', + 'wchar_t', + 'auto', + 'size_t', + 'uint8_t', + 'uint16_t', + 'uint32_t', + 'uint64_t', + 'int8_t', + 'int16_t', + 'int32_t', + 'int64_t', + 'ptrdiff_t', + 'intptr_t', + 'uintptr_t', +]); + +function isCudaPrimitiveType(typeName: string): boolean { + const base = typeName.split(/\s+/).pop() ?? typeName; + return CUDA_PRIMITIVE_TYPES.has(base) || CUDA_PRIMITIVE_TYPES.has(typeName); +} + function extractCudaEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] { const entries: SubDeclaration[] = []; const body = findChild(enumNode, 'enumerator_list');