fix(cpp): extract union specifiers as class-like type nodes

(cherry picked from commit b6c9c34359)
This commit is contained in:
rajatnagda45 authored and safishamsi committed 2026-10-04 20:23:13 +01:00
1 parent 11736158a4
commit 9ca6298761
2 files changed
+46 -2

No files matched your search

+7 -2
View File
@@ -1359,8 +1359,13 @@ _CPP_CONFIG = LanguageConfig(
# set of enumerators. Its name and body sit on the same `name`/`body` fields
# as struct_specifier (type_identifier + enumerator_list), so it gets a node
# and a body walk; the enumerators are emitted by _cpp_extra_walk (the C++
# parity of Java #1719 / Swift / Scala enums).
class_types=frozenset({"class_specifier", "struct_specifier", "enum_specifier"}),
# parity of Java #1719 / Swift / Scala enums). union_specifier is likewise a
# class-like container with the identical name/body fields (type_identifier +
# field_declaration_list), so a `union { ... }` becomes a type node with its
# data members instead of being dropped along with everything it declares.
class_types=frozenset({
"class_specifier", "struct_specifier", "enum_specifier", "union_specifier",
}),
function_types=frozenset({"function_definition"}),
import_types=frozenset({"preproc_include"}),
call_types=frozenset({"call_expression"}),
+39
View File
@@ -84,6 +84,45 @@ def test_cpp_enum_nested_in_class_and_namespace_is_extracted(tmp_path):
assert ("Proto", "Udp") in case_of
def test_cpp_union_specifier_is_extracted(tmp_path):
"""A named `union` is a class-like container whose type node and data members
must survive. `union_specifier` was missing from the C++ class_types, so a
`union { ... }` and everything it declared produced no nodes at all — the whole
type vanished. It shares struct_specifier's name/body fields (type_identifier +
field_declaration_list), so it must get a type node and its members, like a
struct. An anonymous union has no type name and is skipped, like an anonymous
enum.
"""
p = tmp_path / "value.hpp"
p.write_text(
"union Value { int i; float f; };\n"
"struct Point { int x; };\n"
"typedef union { int a; char b; } Anon;\n"
)
result = extract_cpp(p)
assert result.get("parse_errors") is None
ids = {n["id"]: n["label"] for n in result["nodes"]}
labels = set(ids.values())
# Pre-fix the whole `union Value { ... }` declaration vanished.
assert {"Value", "i", "f"} <= labels
contains = {
(ids.get(e["source"]), ids.get(e["target"]))
for e in result["edges"]
if e["relation"] == "contains"
}
defines = {
(ids.get(e["source"]), ids.get(e["target"]))
for e in result["edges"]
if e["relation"] == "defines"
}
assert ("value.hpp", "Value") in contains
assert ("Value", "i") in defines
assert ("Value", "f") in defines
# An anonymous union (`typedef union { ... } Anon`) has no type name, so it is
# skipped rather than emitting a nameless node, like an anonymous enum.
assert "Anon" not in labels
def test_nested_cpp_class_is_extracted(tmp_path):
# A nested type is a field_declaration whose `type` field IS the
# class_specifier; the member-variable branch used to consume it and return