Refactor doxygen xml parser (#56084)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/56084

Changelog: [Internal]

Replaces a wall of if-else statements with a map (compound_type -> parser method).

Reviewed By: cipolleschi

Differential Revision: D96455723

fbshipit-source-id: bd44c02a4a8d08bf9abf463bacdfa0f77615dcff
This commit is contained in:
Dawid Małecki
2026-03-18 09:29:02 -07:00
committed by meta-codesync[bot]
parent b3032d866c
commit 3a14967f81
+117 -91
View File
@@ -28,6 +28,118 @@ from .snapshot import Snapshot
from .utils import has_scope_resolution_outside_angles, parse_qualified_path
def _process_namespace_sections(snapshot, namespace_scope, compound_object):
"""
Process all section definitions inside a namespace compound.
"""
for section_def in compound_object.sectiondef:
if section_def.kind == "var":
for variable_def in section_def.memberdef:
# Skip out-of-class definitions (e.g. "Strct<T>::VALUE")
if has_scope_resolution_outside_angles(variable_def.get_name()):
continue
is_static = variable_def.static == "yes"
namespace_scope.add_member(
get_variable_member(variable_def, "public", is_static)
)
elif section_def.kind == "func":
for function_def in section_def.memberdef:
# Skip out-of-class definitions (e.g. "Strct<T>::convert")
if has_scope_resolution_outside_angles(function_def.get_name()):
continue
function_static = function_def.static == "yes"
if not function_static:
namespace_scope.add_member(
get_function_member(function_def, "public")
)
elif section_def.kind == "typedef":
for typedef_def in section_def.memberdef:
namespace_scope.add_member(get_typedef_member(typedef_def, "public"))
elif section_def.kind == "enum":
for enum_def in section_def.memberdef:
create_enum_scope(snapshot, enum_def)
else:
print(
f"Unknown section kind: {section_def.kind} in {compound_object.location.file}"
)
def _handle_namespace_compound(snapshot, compound_object):
"""
Handle a namespace compound definition.
"""
# Skip anonymous namespaces (internal linkage, not public API).
# Doxygen encodes them with a '@' prefix in the compound name.
if "@" in compound_object.compoundname:
return
namespace_scope = snapshot.create_or_get_namespace(compound_object.compoundname)
namespace_scope.location = compound_object.location.file
_process_namespace_sections(snapshot, namespace_scope, compound_object)
def _handle_concept_compound(snapshot, compound_object):
"""
Handle a concept compound definition.
"""
# Concepts belong to a namespace, so we need to find or create the parent namespace
concept_name = compound_object.compoundname
concept_path = parse_qualified_path(concept_name)
namespace_path = "::".join(concept_path[:-1]) if concept_path else ""
if namespace_path:
namespace_scope = snapshot.create_or_get_namespace(namespace_path)
else:
namespace_scope = snapshot.root_scope
namespace_scope.add_member(get_concept_member(compound_object))
def _handle_class_compound(snapshot, compound_object):
"""
Handle class, struct, and union compound definitions.
"""
# Check if this is an Objective-C interface by looking at the compound id
# Doxygen reports ObjC interfaces as kind="class" but with id starting with "interface"
is_objc_interface = (
compound_object.kind == "class" and compound_object.id.startswith("interface")
)
# Handle Objective-C interfaces separately
if is_objc_interface:
create_interface_scope(snapshot, compound_object)
return
# classes and structs are represented by the same scope with a different kind
create_class_scope(snapshot, compound_object)
# Dispatch table for compound kinds that map directly to a single builder call.
_COMPOUND_HANDLERS = {
"class": _handle_class_compound,
"struct": _handle_class_compound,
"union": _handle_class_compound,
"namespace": _handle_namespace_compound,
"concept": _handle_concept_compound,
"category": create_category_scope,
"protocol": create_protocol_scope,
"interface": create_interface_scope,
}
# Compound kinds that are intentionally ignored.
_IGNORED_COMPOUNDS = frozenset(
{
"file",
"dir",
# Contains deprecation info
"page",
}
)
def build_snapshot(xml_dir: str) -> Snapshot:
"""
Reads the Doxygen XML output and builds a snapshot of the C++ API.
@@ -51,100 +163,14 @@ def build_snapshot(xml_dir: str) -> Snapshot:
if compound_object.prot == "private":
continue
# Check if this is an Objective-C interface by looking at the compound id
# Doxygen reports ObjC interfaces as kind="class" but with id starting with "interface"
is_objc_interface = (
compound_object.kind == "class"
and compound_object.id.startswith("interface")
)
kind = compound_object.kind
# classes and structs are represented by the same scope with a different kind
if (
compound_object.kind == "class"
or compound_object.kind == "struct"
or compound_object.kind == "union"
):
# Handle Objective-C interfaces separately
if is_objc_interface:
create_interface_scope(snapshot, compound_object)
continue
create_class_scope(snapshot, compound_object)
elif compound_object.kind == "namespace":
# Skip anonymous namespaces (internal linkage, not public API).
# Doxygen encodes them with a '@' prefix in the compound name.
if "@" in compound_object.compoundname:
continue
namespace_scope = snapshot.create_or_get_namespace(
compound_object.compoundname
)
namespace_scope.location = compound_object.location.file
for section_def in compound_object.sectiondef:
if section_def.kind == "var":
for variable_def in section_def.memberdef:
# Skip out-of-class definitions (e.g. "Strct<T>::VALUE")
if has_scope_resolution_outside_angles(
variable_def.get_name()
):
continue
is_static = variable_def.static == "yes"
namespace_scope.add_member(
get_variable_member(variable_def, "public", is_static)
)
elif section_def.kind == "func":
for function_def in section_def.memberdef:
# Skip out-of-class definitions (e.g. "Strct<T>::convert")
if has_scope_resolution_outside_angles(
function_def.get_name()
):
continue
function_static = function_def.static == "yes"
if not function_static:
namespace_scope.add_member(
get_function_member(function_def, "public")
)
elif section_def.kind == "typedef":
for typedef_def in section_def.memberdef:
namespace_scope.add_member(
get_typedef_member(typedef_def, "public")
)
elif section_def.kind == "enum":
for enum_def in section_def.memberdef:
create_enum_scope(snapshot, enum_def)
else:
print(
f"Unknown section kind: {section_def.kind} in {compound_object.location.file}"
)
elif compound_object.kind == "concept":
# Concepts belong to a namespace, so we need to find or create the parent namespace
concept_name = compound_object.compoundname
concept_path = parse_qualified_path(concept_name)
namespace_path = "::".join(concept_path[:-1]) if concept_path else ""
if namespace_path:
namespace_scope = snapshot.create_or_get_namespace(namespace_path)
else:
namespace_scope = snapshot.root_scope
namespace_scope.add_member(get_concept_member(compound_object))
elif compound_object.kind == "file":
if kind in _IGNORED_COMPOUNDS:
pass
elif compound_object.kind == "dir":
pass
elif compound_object.kind == "category":
create_category_scope(snapshot, compound_object)
elif compound_object.kind == "page":
# Contains deprecation info
pass
elif compound_object.kind == "protocol":
create_protocol_scope(snapshot, compound_object)
elif compound_object.kind == "interface":
create_interface_scope(snapshot, compound_object)
elif kind in _COMPOUND_HANDLERS:
_COMPOUND_HANDLERS[kind](snapshot, compound_object)
else:
print(f"Unknown compound kind: {compound_object.kind}")
print(f"Unknown compound kind: {kind}")
snapshot.finish()
return snapshot