Files
codebase-memory-mcp/tests
Martin Vogel 44efa6281b fix(extract): C typedefs become nodes; macros and enumerators get their own QNs
In C-family code several different entities competed for one node, and
some had no node at all:

- A typedef name was never a definition: `type_definition` has no `name`
  field, so the class path dropped it. With an anonymous
  `typedef struct/enum {...} X;` the fields and enumerators went with it.
- A function behind an unknown leading macro (`API RetT name(...)`) was
  named after its return type, became a Method `RetT.name`, or was
  dropped, depending on the error-recovery shape.
- Every bodyless `struct X` / `enum X` (a parameter, a field type, a
  forward declaration) minted a Class/Enum node. In one file, or in a
  same-stem .h/.c pair, such a reference displaced the real definition.
- A `#define NAME` shared the QN of the function, type or enumerator
  called NAME, and the later line won: a namespace-rename macro replaced
  the typedef it renames, an `#else` stub macro replaced the function.
- Enumerators were named `<scope>.<Enum>.<CONST>`, although C and
  unscoped C++ enums put them in the enclosing scope.
- A definition repeated in several `#if` branches kept one span; the
  others left no trace.
- Braces split across `#if` branches lost whole functions, and a C
  keyword could surface as a function name (`if`).

Extraction:
- typedef names are definitions: `Type`, or `Class`/`Enum` with members
  for an anonymous `typedef struct/enum {...} X;`; an alias is dropped
  when the same file defines the tag of that name
- macro-prefixed functions get their real name in all three recovery
  shapes, consistently for defs, call scopes, parameters and the C LSP
- a bodyless tag is a reference and creates no node; a definition head
  left as loose ERROR tokens (`struct NAME {`) is recovered
- a C keyword is never a function name
- a first-branch projection (one branch per `#if` group, positions
  unchanged) is parsed when the raw parse lost structure wholesale, and
  only functions missing from the raw result are adopted from it
- macro-wrapped enumerator lists yield one constant per slot

Identity (semantic index version 4; an existing index is rebuilt once):
- a C-preprocessor macro's QN is `<module>.<NAME>#macro` (C, C++, CUDA,
  GLSL, Objective-C, ISPC); name and label are unchanged. Resolvers
  prefer a definition and use the macro only when no definition of that
  name is visible (LSP target lookup, trace_path, get_code_snippet)
- enumerators of C enums and of unscoped C++/Objective-C enums are
  `<enclosing scope>.<CONST>`; `enum class` stays nested; `parent_class`
  names the enum
- a node whose file defines its QN more than once under one label
  carries `variants`, the line spans of all those definitions (`#if`
  twins, per-platform macro redefinitions, C++ overloads)

Full-text search indexes a macro's QN without the fence, so macro rows
score as before.

Measured on redis and curl against the parent commit: nodes 38,625 ->
40,484 and 28,180 -> 29,750; Type nodes 0 -> 573 and 0 -> 193; phantom
Class nodes from references 202 and 821 removed; functions lost to a
same-named macro 45 and 41 restored; curl functions with no node 11 ->
0; nodes of non-C-family files unchanged by QN. Of 174 audited Doxygen
references in xxhash, 174 now reach the right entity (64 before). Index
time is unchanged within noise (+2% pipeline time for 5% more nodes).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-02 06:37:36 +02:00
..