fix(c): blank leading attribute macros so functions index under real names (#1311)
SEC_ATTR UINT32 LostName(VOID) — an unknown attribute macro before a
typedef'd return type — misparses in tree-sitter's C grammar: the macro
becomes the type, the return type the declarator, and the PARAMETER
LIST is stored as the function name ("(VOID)"). The C++ grammar
recovers this shape via recoverMangledCppName, but in C the real name
never reaches the mangled string, so only a pre-parse blank can help.
Attribute macros are project-specific, so the blank keys on structure:
line-leading ALL-CAPS token followed by TWO identifiers then `(` — the
`MACRO Ret name(` definition shape. Plain typedef'd returns, ALL-CAPS
calls, #define lines, multi-word builtin returns, and mid-line uses are
all rejected by construction. Offset-preserving like the C++ blanks.
curl re-index: 5,531 C functions before and after, zero name changes;
7 nodes in memdebug.c improve start-line accuracy by 1 (the macro line
no longer counts as part of the definition).
Fixes #1211
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e437918026
commit
b6a05d155b
@@ -11,7 +11,7 @@ import * as os from 'os';
|
||||
import { CodeGraph } from '../src';
|
||||
import { extractFromSource, scanDirectory, buildDefaultIgnore, discoverEmbeddedRepoRoots, buildScopeIgnore } from '../src/extraction';
|
||||
import { detectLanguage, isLanguageSupported, getSupportedLanguages, initGrammars, loadAllGrammars, isSourceFile } from '../src/extraction/grammars';
|
||||
import { stripCppTemplateArgs, blankCppExportMacros, blankCppInlineMacros, blankMetalAttributes, blankCudaConstructs, blankCppAnnotationMacroCalls, blankCppApiPrefixMacros, blankCppInlineAnnotationMacros, recoverMangledCppName } from '../src/extraction/languages/c-cpp';
|
||||
import { stripCppTemplateArgs, blankCppExportMacros, blankCppInlineMacros, blankMetalAttributes, blankCudaConstructs, blankCppAnnotationMacroCalls, blankCppApiPrefixMacros, blankCppInlineAnnotationMacros, blankCLeadingAttrMacros, recoverMangledCppName } from '../src/extraction/languages/c-cpp';
|
||||
import { normalizePath } from '../src/utils';
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -4115,6 +4115,59 @@ class Both : public Base<char>, public Plain {};
|
||||
});
|
||||
});
|
||||
|
||||
describe('C leading attribute macro before typedef return type (#1211)', () => {
|
||||
// `SEC_ATTR UINT32 LostName(VOID)` — tree-sitter's C grammar reads the
|
||||
// unknown macro as the type, the typedef'd return as the declarator, and
|
||||
// stores the PARAMETER LIST as the function name ("(VOID)"). The
|
||||
// structural pre-parse blank recovers the definition; the issue's whole
|
||||
// isolation table is pinned here.
|
||||
it("recovers the issue's full isolation table under their real names", () => {
|
||||
const code = `#define SEC_ATTR __attribute__((section(".init")))
|
||||
typedef unsigned int UINT32;
|
||||
#define VOID void
|
||||
|
||||
SEC_ATTR VOID GoodName(VOID) { }
|
||||
SEC_ATTR UINT32 LostName(VOID) { return 0; }
|
||||
UINT32 NoAttr(void) { return 0; }
|
||||
SEC_ATTR int BuiltinRet(void) { return 0; }
|
||||
__attribute__((section(".init"))) UINT32 RawAttr(void) { return 0; }
|
||||
SEC_ATTR UINT32 OneNamedArg(UINT32 x) { return x; }
|
||||
SEC_ATTR UINT32* PtrRet(VOID) { return 0; }
|
||||
`;
|
||||
const result = extractFromSource('attrs.c', code);
|
||||
const fns = result.nodes.filter((n) => n.kind === 'function').map((n) => n.name);
|
||||
expect(fns).toEqual(
|
||||
expect.arrayContaining([
|
||||
'GoodName', 'LostName', 'NoAttr', 'BuiltinRet', 'RawAttr', 'OneNamedArg', 'PtrRet',
|
||||
])
|
||||
);
|
||||
// The bug shape: a parameter list stored as a name.
|
||||
expect(fns.find((n) => n.includes('('))).toBeUndefined();
|
||||
});
|
||||
|
||||
it('blankCLeadingAttrMacros only touches the MACRO-ret-name-( definition shape', () => {
|
||||
// Blanked: the definition shape (offset-preserving).
|
||||
expect(blankCLeadingAttrMacros('SEC_ATTR UINT32 f(void) {}')).toBe(
|
||||
' UINT32 f(void) {}'
|
||||
);
|
||||
// Untouched: a plain typedef'd return with ONE identifier before `(`.
|
||||
expect(blankCLeadingAttrMacros('UINT32 helper(void) {}')).toBe('UINT32 helper(void) {}');
|
||||
// Untouched: an ALL-CAPS function CALL at line start.
|
||||
expect(blankCLeadingAttrMacros('MY_ASSERT(x);')).toBe('MY_ASSERT(x);');
|
||||
// Untouched: #define lines (start with #, not line-leading CAPS).
|
||||
const def = '#define SEC_ATTR __attribute__((section(".init")))';
|
||||
expect(blankCLeadingAttrMacros(def)).toBe(def);
|
||||
// Untouched: multi-word builtin returns (the grammar keeps the name there).
|
||||
expect(blankCLeadingAttrMacros('SEC_ATTR unsigned int f(void) {}')).toBe(
|
||||
'SEC_ATTR unsigned int f(void) {}'
|
||||
);
|
||||
// Untouched: mid-line uses.
|
||||
expect(blankCLeadingAttrMacros('x = SEC_ATTR UINT32 y(z);')).toBe(
|
||||
'x = SEC_ATTR UINT32 y(z);'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('C++ out-of-line template method receivers (#1286)', () => {
|
||||
// `template<typename T> T Box<T>::get()` used to store qualified_name
|
||||
// `Box<T>::get` — the `<T>` qualifier never matched the class node indexed
|
||||
|
||||
Reference in New Issue
Block a user