fix(extraction): drop the phantom C++ function from a macro-annotated class misparse (#946) (#948)

A C++ class/struct annotated with an export/visibility macro —
`class MYLIB_EXPORT Foo : public Bar { … }` — makes tree-sitter read
`class MYLIB_EXPORT` as an elaborated type specifier and the whole declaration
as a `function_definition` named after the class, spanning the entire body. That
phantom `function` polluted callers/impact/blast-radius and skewed kind stats.

Detect the misparse structurally in cppExtractor.isMisparsedFunction — a
function_definition whose `type` field is a *bodyless* class/struct specifier
(the elaborated-type macro) and whose declarator is not a function_declarator —
and drop the bogus node, matching how macro-prefixed C prototypes are already
handled. The body is mangled by the same misparse and is unrecoverable. Precise
enough to leave genuine code alone: `struct P { int x; } makeP() {}` (real
inline-defined return type, has a field list) and `class Foo f() {}` (elaborated
return type on a real function, has a function_declarator) are untouched. The
leading macro alone triggers the misparse; a base clause is not required.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-22 08:45:50 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 2bdc169ce4
commit f63e5db2cc
3 changed files with 97 additions and 2 deletions
+57
View File
@@ -2582,6 +2582,63 @@ std::unique_ptr<Widget> makeWidget() { return nullptr; }
});
});
describe('C++ macro-prefixed class/struct misparse (#946)', () => {
// An export/visibility macro before the class name plus a base clause
// (`class MACRO Name : public Base { … }`) makes tree-sitter read `class
// MACRO` as an elaborated type and the whole declaration as a
// function_definition named after the class, spanning the entire body — a
// phantom `function` that polluted callers/impact/blast-radius. It's dropped.
it('does not mint a phantom function for a macro-annotated class that inherits', () => {
const code = `#pragma once
#define MAPCORE_EXPORT __attribute__((visibility("default")))
class DataProvider {
public:
virtual bool Request(void* param) = 0;
};
class MAPCORE_EXPORT LocalDataProvider : public DataProvider
{
public:
LocalDataProvider(int dataType);
virtual bool Request(void* param) override;
};
`;
// A header rich in C++ (class / public: / virtual) detects as C++ — the
// issue's exact scenario (a `.h` file). Guard it so a detection regression
// can't make this test pass for the wrong reason.
expect(detectLanguage('provider.h', code)).toBe('cpp');
const result = extractFromSource('provider.h', code);
// The misparse used to surface as `function | LocalDataProvider` spanning
// the whole class body — a false caller in the graph. It's gone now.
expect(
result.nodes.find((n) => n.name === 'LocalDataProvider' && n.kind === 'function')
).toBeUndefined();
// The sibling class without the macro is unaffected — still a class.
expect(result.nodes.find((n) => n.name === 'DataProvider')?.kind).toBe('class');
});
it('drops the struct variant too, without dropping a genuine class', () => {
const code = `
#define API __declspec(dllexport)
struct API Widget : public Base { int x; };
class Plain : public Base { public: int y; };
`;
const result = extractFromSource('widget.cpp', code);
// `struct MACRO Name : Base { … }` misparses the same way — no phantom function.
expect(
result.nodes.find((n) => n.name === 'Widget' && n.kind === 'function')
).toBeUndefined();
// A normal class with a base clause and no macro must still be a class — the
// drop is precise, not a blanket "class with inheritance" filter.
expect(result.nodes.find((n) => n.name === 'Plain')?.kind).toBe('class');
});
});
describe('C/C++ imports', () => {
it('should extract system include', () => {
const code = `#include <iostream>`;