Skip to content

Commit 3a14967

Browse files
coadometa-codesync[bot]
authored andcommitted
Refactor doxygen xml parser (#56084)
Summary: Pull Request resolved: #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
1 parent b3032d8 commit 3a14967

File tree

1 file changed

+117
-91
lines changed

1 file changed

+117
-91
lines changed

scripts/cxx-api/parser/main.py

Lines changed: 117 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,118 @@
2828
from .utils import has_scope_resolution_outside_angles, parse_qualified_path
2929

3030

31+
def _process_namespace_sections(snapshot, namespace_scope, compound_object):
32+
"""
33+
Process all section definitions inside a namespace compound.
34+
"""
35+
for section_def in compound_object.sectiondef:
36+
if section_def.kind == "var":
37+
for variable_def in section_def.memberdef:
38+
# Skip out-of-class definitions (e.g. "Strct<T>::VALUE")
39+
if has_scope_resolution_outside_angles(variable_def.get_name()):
40+
continue
41+
is_static = variable_def.static == "yes"
42+
namespace_scope.add_member(
43+
get_variable_member(variable_def, "public", is_static)
44+
)
45+
elif section_def.kind == "func":
46+
for function_def in section_def.memberdef:
47+
# Skip out-of-class definitions (e.g. "Strct<T>::convert")
48+
if has_scope_resolution_outside_angles(function_def.get_name()):
49+
continue
50+
function_static = function_def.static == "yes"
51+
52+
if not function_static:
53+
namespace_scope.add_member(
54+
get_function_member(function_def, "public")
55+
)
56+
elif section_def.kind == "typedef":
57+
for typedef_def in section_def.memberdef:
58+
namespace_scope.add_member(get_typedef_member(typedef_def, "public"))
59+
elif section_def.kind == "enum":
60+
for enum_def in section_def.memberdef:
61+
create_enum_scope(snapshot, enum_def)
62+
else:
63+
print(
64+
f"Unknown section kind: {section_def.kind} in {compound_object.location.file}"
65+
)
66+
67+
68+
def _handle_namespace_compound(snapshot, compound_object):
69+
"""
70+
Handle a namespace compound definition.
71+
"""
72+
# Skip anonymous namespaces (internal linkage, not public API).
73+
# Doxygen encodes them with a '@' prefix in the compound name.
74+
if "@" in compound_object.compoundname:
75+
return
76+
77+
namespace_scope = snapshot.create_or_get_namespace(compound_object.compoundname)
78+
79+
namespace_scope.location = compound_object.location.file
80+
81+
_process_namespace_sections(snapshot, namespace_scope, compound_object)
82+
83+
84+
def _handle_concept_compound(snapshot, compound_object):
85+
"""
86+
Handle a concept compound definition.
87+
"""
88+
# Concepts belong to a namespace, so we need to find or create the parent namespace
89+
concept_name = compound_object.compoundname
90+
concept_path = parse_qualified_path(concept_name)
91+
namespace_path = "::".join(concept_path[:-1]) if concept_path else ""
92+
93+
if namespace_path:
94+
namespace_scope = snapshot.create_or_get_namespace(namespace_path)
95+
else:
96+
namespace_scope = snapshot.root_scope
97+
98+
namespace_scope.add_member(get_concept_member(compound_object))
99+
100+
101+
def _handle_class_compound(snapshot, compound_object):
102+
"""
103+
Handle class, struct, and union compound definitions.
104+
"""
105+
# Check if this is an Objective-C interface by looking at the compound id
106+
# Doxygen reports ObjC interfaces as kind="class" but with id starting with "interface"
107+
is_objc_interface = (
108+
compound_object.kind == "class" and compound_object.id.startswith("interface")
109+
)
110+
111+
# Handle Objective-C interfaces separately
112+
if is_objc_interface:
113+
create_interface_scope(snapshot, compound_object)
114+
return
115+
116+
# classes and structs are represented by the same scope with a different kind
117+
create_class_scope(snapshot, compound_object)
118+
119+
120+
# Dispatch table for compound kinds that map directly to a single builder call.
121+
_COMPOUND_HANDLERS = {
122+
"class": _handle_class_compound,
123+
"struct": _handle_class_compound,
124+
"union": _handle_class_compound,
125+
"namespace": _handle_namespace_compound,
126+
"concept": _handle_concept_compound,
127+
"category": create_category_scope,
128+
"protocol": create_protocol_scope,
129+
"interface": create_interface_scope,
130+
}
131+
132+
# Compound kinds that are intentionally ignored.
133+
_IGNORED_COMPOUNDS = frozenset(
134+
{
135+
"file",
136+
"dir",
137+
# Contains deprecation info
138+
"page",
139+
}
140+
)
141+
142+
31143
def build_snapshot(xml_dir: str) -> Snapshot:
32144
"""
33145
Reads the Doxygen XML output and builds a snapshot of the C++ API.
@@ -51,100 +163,14 @@ def build_snapshot(xml_dir: str) -> Snapshot:
51163
if compound_object.prot == "private":
52164
continue
53165

54-
# Check if this is an Objective-C interface by looking at the compound id
55-
# Doxygen reports ObjC interfaces as kind="class" but with id starting with "interface"
56-
is_objc_interface = (
57-
compound_object.kind == "class"
58-
and compound_object.id.startswith("interface")
59-
)
60-
61-
# classes and structs are represented by the same scope with a different kind
62-
if (
63-
compound_object.kind == "class"
64-
or compound_object.kind == "struct"
65-
or compound_object.kind == "union"
66-
):
67-
# Handle Objective-C interfaces separately
68-
if is_objc_interface:
69-
create_interface_scope(snapshot, compound_object)
70-
continue
71-
create_class_scope(snapshot, compound_object)
72-
elif compound_object.kind == "namespace":
73-
# Skip anonymous namespaces (internal linkage, not public API).
74-
# Doxygen encodes them with a '@' prefix in the compound name.
75-
if "@" in compound_object.compoundname:
76-
continue
166+
kind = compound_object.kind
77167

78-
namespace_scope = snapshot.create_or_get_namespace(
79-
compound_object.compoundname
80-
)
81-
82-
namespace_scope.location = compound_object.location.file
83-
84-
for section_def in compound_object.sectiondef:
85-
if section_def.kind == "var":
86-
for variable_def in section_def.memberdef:
87-
# Skip out-of-class definitions (e.g. "Strct<T>::VALUE")
88-
if has_scope_resolution_outside_angles(
89-
variable_def.get_name()
90-
):
91-
continue
92-
is_static = variable_def.static == "yes"
93-
namespace_scope.add_member(
94-
get_variable_member(variable_def, "public", is_static)
95-
)
96-
elif section_def.kind == "func":
97-
for function_def in section_def.memberdef:
98-
# Skip out-of-class definitions (e.g. "Strct<T>::convert")
99-
if has_scope_resolution_outside_angles(
100-
function_def.get_name()
101-
):
102-
continue
103-
function_static = function_def.static == "yes"
104-
105-
if not function_static:
106-
namespace_scope.add_member(
107-
get_function_member(function_def, "public")
108-
)
109-
elif section_def.kind == "typedef":
110-
for typedef_def in section_def.memberdef:
111-
namespace_scope.add_member(
112-
get_typedef_member(typedef_def, "public")
113-
)
114-
elif section_def.kind == "enum":
115-
for enum_def in section_def.memberdef:
116-
create_enum_scope(snapshot, enum_def)
117-
else:
118-
print(
119-
f"Unknown section kind: {section_def.kind} in {compound_object.location.file}"
120-
)
121-
elif compound_object.kind == "concept":
122-
# Concepts belong to a namespace, so we need to find or create the parent namespace
123-
concept_name = compound_object.compoundname
124-
concept_path = parse_qualified_path(concept_name)
125-
namespace_path = "::".join(concept_path[:-1]) if concept_path else ""
126-
127-
if namespace_path:
128-
namespace_scope = snapshot.create_or_get_namespace(namespace_path)
129-
else:
130-
namespace_scope = snapshot.root_scope
131-
132-
namespace_scope.add_member(get_concept_member(compound_object))
133-
elif compound_object.kind == "file":
134-
pass
135-
elif compound_object.kind == "dir":
136-
pass
137-
elif compound_object.kind == "category":
138-
create_category_scope(snapshot, compound_object)
139-
elif compound_object.kind == "page":
140-
# Contains deprecation info
168+
if kind in _IGNORED_COMPOUNDS:
141169
pass
142-
elif compound_object.kind == "protocol":
143-
create_protocol_scope(snapshot, compound_object)
144-
elif compound_object.kind == "interface":
145-
create_interface_scope(snapshot, compound_object)
170+
elif kind in _COMPOUND_HANDLERS:
171+
_COMPOUND_HANDLERS[kind](snapshot, compound_object)
146172
else:
147-
print(f"Unknown compound kind: {compound_object.kind}")
173+
print(f"Unknown compound kind: {kind}")
148174

149175
snapshot.finish()
150176
return snapshot

0 commit comments

Comments
 (0)