Repository navigation
Keep C and C++ functions that return pointers or references in the graph - #148
Merged
Merged
Conversation
…he graph The C and C++ analyzers looked for a function's function_declarator as a direct child of its function_definition. When the return type is a pointer (`const char *f()`, `T **f()`) or a C++ reference (`T &f()`), tree-sitter wraps it in a pointer/reference declarator, so the function was left out of the dependency graph, together with every call made from inside it. A function returning a function pointer was also lost. An export macro before a typedef'd return type (`API lua_Integer f()`) was named after its return type: tree-sitter reads the type as the declarator and leaves the real name in an ERROR node before the parameters. Both analyzers now find the declarator through those wrappers (shared helper in analyzers/c_declarators.py) and read the macro case's name from the ERROR node.
…s name
`int CJSON_CDECL main(void) { ... }`: with a parameter list of one word, tree-sitter-c reads `int
CJSON_CDECL` as a declaration missing its `;`, then a function_definition whose type is `main` and whose
declarator is the parenthesized `(void)`. It has no function_declarator, so the function and every call
made from it were left out. (With real parameters the macro lands in an ERROR node and the name is found,
as before.)
split_macro_function_name: a function_definition whose type is a type_identifier, whose declarator is a
parenthesized single identifier, and whose previous sibling is a declaration ending in a missing `;` on
the same line, defines a function named by that type. A real typedef'd return type (`Count two(void)`)
follows a complete declaration, so it is not taken for a name.
Measured (old branch head against this commit, components): cJSON 169 -> 170 (test.c::main); lz4, lua,
inih, tinyxml2 and jq unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The C and C++ analyzers looked for a function's
function_declaratoras a direct child of itsfunction_definition. When the return type is a pointer (const char *f(),T **f()) or a C++ reference (T &f()), tree-sitter wraps the declarator in apointer_declarator/reference_declarator, so the function was left out of the dependency graph, together with every call made from inside it. For lua, 162 of its ~1,300 C functions were missing, including public API such aslua_tolstring,lua_pushstringandluaL_checklstring.A second case: with an export macro before a typedef'd return type (
LUA_API lua_Unsigned lua_rawlen(...)), tree-sitter reads the type as the declarator and leaves the real name in anERRORnode before the parameters. The function was recorded under its return type's name (lapi.c::lua_Unsigned), and functions with the same return type collided.Changes
analyzers/c_declarators.py, shared by both analyzers:function_declarator()finds the declarator through pointer, reference, parenthesized and attributed declarators. For a function that returns a function pointer, it takes the innermost one.macro_typed_name()reads the real name from theERRORnode in the macro case.c.py: both function extraction and_find_containing_functionuse the helpers, so calls from inside these functions get their edges.cpp.py: every place that looked up thefunction_declaratorof a definition or method declaration uses the helper._find_containing_functionuses_get_declarator_name, so qualified names work there too.Measurements without LLM calls
Dependency graph built with
DependencyParser(repo).parse_repository(), comparingmain(1b23ea3) with this branch. "Missing" counts function definitions found by a rough regex in files the parser reads, with no component of that name in the same file.The names that were wrong are gone:
lapi.c::lua_CFunction,lapi.c::lua_Unsigned,lauxlib.c::lua_Integer(lua), andlib/xxhash.c::XXH_errorcode(lz4).One function is still missing: cJSON's
int CJSON_CDECL main(void). With a calling-convention macro between the type and the name, tree-sitter splits the head into a declaration plus a broken definition. Recovering it would need preprocessing.Validation
pytest -p no:cacheprovider -o addopts="" tests/ -q).tests/test_c_cpp_declarators.pycovers functions returningT *,T **,const char *, a function pointer,API T f()in C, andT &/const char *methods,T **,API T f()in C++. It also checks the call edges made from inside them. Both tests fail onmain.uvx ruff@latest checkanduvx ruff@latest format --checkpassed on the 4 changed files;git diff --checkpassed.