From 05311b59498ac24a7ad5644930cd0d92bf6d750c Mon Sep 17 00:00:00 2001 From: li Date: Thu, 9 Jul 2026 10:09:23 +0800 Subject: [PATCH] fix(extraction): C extractor now blanks macro attributes before typedef return types (#1211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tree-sitter-c can't reconcile an unknown macro token before a typedef'd return type — `SEC_ATTR UINT32 LostName(VOID)` misparsed the function name as `(VOID)` instead of `LostName`. This affected all C projects using section/placement macros (OpenHarmony LiteOS-M, embedded firmware, etc.). Two additions to preParseCSource: 1. blankCLeadingMacros — blanks arbitrary ALL_CAPS tokens sitting before a known C return type at line start (handles project-specific macros like SEC_ATTR, LITE_OS_SEC_TEXT_INIT that no curated list can cover). 2. Wire in blankCppInlineMacros — catches known inline-specifier macros already curated for C++ (SQLITE_API, WINAPI, G_INLINE_FUNC, etc.) that are equally common in C codebases. Closes #1211 --- __tests__/extraction.test.ts | 34 +++++++++++++++++++++++++++++++ src/extraction/languages/c-cpp.ts | 32 +++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/__tests__/extraction.test.ts b/__tests__/extraction.test.ts index 8619d12d7..28ecff3b2 100644 --- a/__tests__/extraction.test.ts +++ b/__tests__/extraction.test.ts @@ -3964,6 +3964,40 @@ class APXCharacter { // the one real definition }); }); + describe('C macro-attribute + typedef return type misparse (#1211)', () => { + const cNames = (code: string) => + extractFromSource('main.c', code).nodes + .filter((n) => n.kind === 'function') + .map((n) => n.name); + + it('recovers the function name when a macro attribute precedes a typedef return type', () => { + 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; } +`; + const names = cNames(code); + expect(names).toContain('GoodName'); + expect(names).toContain('LostName'); + expect(names).not.toContain('(VOID)'); + }); + + it('handles multiple leading macro tokens before a known return type', () => { + const code = ` +LITE_OS_SEC_TEXT_INIT UINT32 LOS_KernelInit(void) { return 0; } +`; + expect(cNames(code)).toContain('LOS_KernelInit'); + }); + + it('does not blank a real mixed-case return type', () => { + const code = `int normalFunc(void) { return 0; }`; + expect(cNames(code)).toContain('normalFunc'); + }); + }); + describe('C++ templated base-class inheritance (#1043)', () => { // Inheriting from a template (`class D : public Base`) recorded the base // ref as the full instantiation `Base`, which never name-matched the diff --git a/src/extraction/languages/c-cpp.ts b/src/extraction/languages/c-cpp.ts index 76a093965..abf9276da 100644 --- a/src/extraction/languages/c-cpp.ts +++ b/src/extraction/languages/c-cpp.ts @@ -701,11 +701,35 @@ function preParseCppSource(source: string, filePath?: string): string { return blanked; } -/** C source pre-processing: C-detected headers in CUDA projects (llm.c keeps - * `__device__` helpers and kernel prototypes in plain `.h`) get the same - * content-gated CUDA blank as C++. */ +/** + * Blank isolated ALL_CAPS macro tokens sitting before a known C return type at + * line start. Handles project-specific attribute/section macros (SEC_ATTR, + * LITE_OS_SEC_TEXT_INIT, etc.) that no curated list can cover. tree-sitter-c + * can't reconcile an unknown token before a typedef'd return type — the + * function name is lost and replaced by the parameter list (#1211). + * + * Matched tightly: ALL_CAPS tokens only, at line start only, and must be + * followed by a known primitive or common typedef return type — so real + * declarations like `HRESULT DoIt()` (mixed-case) are never touched. + */ +const C_LEADING_MACRO_RE = + /^(\s*)((?:[A-Z_][A-Z0-9_]*\s+)+)((?:unsigned\s+|signed\s+|long\s+|short\s+)?(?:void|int|char|short|long|float|double|bool|size_t|ssize_t|ptrdiff_t|u?int(?:8|16|32|64|ptr)_t|VOID|BOOL|BOOLEAN|BYTE|WORD|DWORD|UINT8|UINT16|UINT32|UINT64|INT8|INT16|INT32|INT64|UINTPTR|CHAR|UCHAR|WCHAR|ULONG|LONG|USHORT|SHORT|HANDLE|HRESULT|NTSTATUS|PVOID|LPVOID)\s)/gm; +function blankCLeadingMacros(source: string): string { + return source.replace( + C_LEADING_MACRO_RE, + (_match, indent: string, macros: string, retType: string) => + indent + ' '.repeat(macros.length) + retType, + ); +} + +/** C source pre-processing: blank macro tokens that tree-sitter-c can't + * reconcile before return types (#1211), plus known inline-specifier macros + * shared with C++ (SQLITE_API, WINAPI, etc.), and CUDA constructs in + * C-detected headers. */ function preParseCSource(source: string): string { - return looksLikeCudaSource(source) ? blankCudaConstructs(source) : source; + let result = blankCLeadingMacros(blankCppInlineMacros(source)); + if (looksLikeCudaSource(source)) result = blankCudaConstructs(result); + return result; } export const cppExtractor: LanguageExtractor = {