Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions code_review_graph/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3975,6 +3975,13 @@ def _get_name(self, node, language: str, kind: str) -> Optional[str]:
if language in ("c", "cpp", "objc") and kind == "function":
for child in node.children:
if child.type in ("function_declarator", "pointer_declarator"):
# Scoped names like Foo::bar use qualified_identifier; take
# the rightmost identifier/field_identifier after the last ::.
for sub in child.children:
if sub.type == "qualified_identifier":
for qsub in reversed(sub.children):
if qsub.type in ("identifier", "field_identifier"):
return qsub.text.decode("utf-8", errors="replace")
result = self._get_name(child, language, kind)
if result:
return result
Expand Down Expand Up @@ -4010,11 +4017,13 @@ def _get_name(self, node, language: str, kind: str) -> Optional[str]:
for sub in child.children:
if sub.type == "type_identifier":
return sub.text.decode("utf-8", errors="replace")
# Most languages use a 'name' child
# Most languages use a 'name' child.
# field_identifier covers C++ class member function names inside
# function_declarator (e.g. virtual std::string get_name() = 0).
for child in node.children:
if child.type in (
"identifier", "name", "type_identifier", "property_identifier",
"simple_identifier", "constant",
"simple_identifier", "constant", "field_identifier",
):
return child.text.decode("utf-8", errors="replace")
# For Go type declarations, look for type_spec
Expand Down
77 changes: 77 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1209,3 +1209,80 @@ def test_elixir_top_level_dotted_call_attributes_to_file(self):
and e.target.endswith("puts")
]
assert len(top_level) == 1

class TestCppScopedFunctionName:
"""Regression tests for C++ scoped function name extraction.

See: https://github.com/tirth8205/code-review-graph/issues/395
"""

def test_scoped_function_with_type_identifier_return(self, tmp_path):
"""bufferlist OSDService::get_inc_map(...) should extract 'get_inc_map'."""
src = tmp_path / "osd_service.cpp"
src.write_text(
"bufferlist OSDService::get_inc_map(epoch_t e) {\n"
" bufferlist bl;\n"
" return bl;\n"
"}\n"
)
p = CodeParser()
nodes, _ = p.parse_file(src)
fns = [n for n in nodes if n.kind == "Function"]
assert len(fns) == 1
assert fns[0].name == "get_inc_map"

def test_scoped_function_with_qualified_return(self, tmp_path):
"""std::string OSDMap::get_pool_name(...) should extract 'get_pool_name'."""
src = tmp_path / "osd_map.cpp"
src.write_text(
"std::string OSDMap::get_pool_name(int64_t pool_id) const {\n"
' return "";\n'
"}\n"
)
p = CodeParser()
nodes, _ = p.parse_file(src)
fns = [n for n in nodes if n.kind == "Function"]
assert len(fns) == 1
assert fns[0].name == "get_pool_name"

def test_scoped_function_with_primitive_return_still_works(self, tmp_path):
"""int OSD::handle_osd_map(...) was already correct; verify no regression."""
src = tmp_path / "osd.cpp"
src.write_text(
"int OSD::handle_osd_map(MOSDMap *m) {\n"
" return 0;\n"
"}\n"
)
p = CodeParser()
nodes, _ = p.parse_file(src)
fns = [n for n in nodes if n.kind == "Function"]
assert len(fns) == 1
assert fns[0].name == "handle_osd_map"

def test_unscoped_function_with_type_identifier_return(self, tmp_path):
"""static std::string _make_key(...) should extract '_make_key'."""
src = tmp_path / "util.cpp"
src.write_text(
"static std::string _make_key(const std::string& prefix) {\n"
" return prefix;\n"
"}\n"
)
p = CodeParser()
nodes, _ = p.parse_file(src)
fns = [n for n in nodes if n.kind == "Function"]
assert len(fns) == 1
assert fns[0].name == "_make_key"

def test_scoped_function_string_return(self, tmp_path):
"""string RGWDedupProcessor::get_obj_fingerprint(...) should extract the method name."""
src = tmp_path / "rgw_dedup.cpp"
src.write_text(
"string RGWDedupProcessor::get_obj_fingerprint(const rgw_obj& obj) {\n"
' return "";\n'
"}\n"
)
p = CodeParser()
nodes, _ = p.parse_file(src)
fns = [n for n in nodes if n.kind == "Function"]
assert len(fns) == 1
assert fns[0].name == "get_obj_fingerprint"