Skip to content

Commit 9ec7db5

Browse files
coadometa-codesync[bot]
authored andcommitted
Add support for parsing interfaces (#55770)
Summary: Pull Request resolved: #55770 This diff adds support for parsing Objective-C interfaces in the C++ API snapshot parser. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D94071995 fbshipit-source-id: b1b89d520a489be7c4a0e790874dd9f7b518df35
1 parent a19d98e commit 9ec7db5

12 files changed

Lines changed: 226 additions & 3 deletions

File tree

scripts/cxx-api/manual_test/.doxygen.config.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,7 @@ INCLUDE_FILE_PATTERNS =
24422442
# recursively expanded use the := operator instead of the = operator.
24432443
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
24442444

2445-
PREDEFINED =
2445+
PREDEFINED = FOLLY_PACK_PUSH="" FOLLY_PACK_POP="" FOLLY_PACK_ATTR="" __attribute__(x)="" ${PREDEFINED}
24462446

24472447
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
24482448
# tag can be used to specify a list of macro names that should be expanded. The

scripts/cxx-api/parser/main.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import os
99
import re
1010
from enum import Enum
11-
from pprint import pprint
1211

1312
from doxmlparser import compound, index
1413

@@ -21,7 +20,7 @@
2120
TypedefMember,
2221
VariableMember,
2322
)
24-
from .scope import ProtocolScopeKind, StructLikeScopeKind
23+
from .scope import InterfaceScopeKind, ProtocolScopeKind, StructLikeScopeKind
2524
from .snapshot import Snapshot
2625
from .template import Template
2726
from .utils import Argument, extract_qualifiers, parse_qualified_path
@@ -608,6 +607,66 @@ def create_protocol_scope(snapshot: Snapshot, scope_def: compound.CompounddefTyp
608607
)
609608

610609

610+
def create_interface_scope(snapshot: Snapshot, scope_def: compound.CompounddefType):
611+
"""
612+
Create an interface scope in the snapshot (Objective-C @interface).
613+
"""
614+
interface_name = scope_def.compoundname
615+
616+
interface_scope = snapshot.create_interface(interface_name)
617+
base_classes = get_base_classes(scope_def, base_class=InterfaceScopeKind.Base)
618+
interface_scope.kind.add_base(base_classes)
619+
interface_scope.location = scope_def.location.file
620+
621+
for section_def in scope_def.sectiondef:
622+
kind = section_def.kind
623+
parts = kind.split("-")
624+
visibility = parts[0]
625+
is_static = "static" in parts
626+
member_type = parts[-1]
627+
628+
if visibility == "private":
629+
pass
630+
elif visibility in ("public", "protected"):
631+
if member_type == "attrib":
632+
for member_def in section_def.memberdef:
633+
if member_def.kind == "variable":
634+
interface_scope.add_member(
635+
get_variable_member(member_def, visibility, is_static)
636+
)
637+
elif member_type == "func":
638+
for function_def in section_def.memberdef:
639+
interface_scope.add_member(
640+
get_function_member(function_def, visibility, is_static)
641+
)
642+
elif member_type == "type":
643+
for member_def in section_def.memberdef:
644+
if member_def.kind == "enum":
645+
create_enum_scope(snapshot, member_def)
646+
elif member_def.kind == "typedef":
647+
interface_scope.add_member(
648+
get_typedef_member(member_def, visibility)
649+
)
650+
else:
651+
print(
652+
f"Unknown section member kind: {member_def.kind} in {scope_def.location.file}"
653+
)
654+
else:
655+
print(
656+
f"Unknown interface section kind: {kind} in {scope_def.location.file}"
657+
)
658+
elif visibility == "property":
659+
for member_def in section_def.memberdef:
660+
if member_def.kind == "property":
661+
interface_scope.add_member(
662+
get_property_member(member_def, "public", is_static)
663+
)
664+
else:
665+
print(
666+
f"Unknown interface visibility: {visibility} in {scope_def.location.file}"
667+
)
668+
669+
611670
def build_snapshot(xml_dir: str) -> Snapshot:
612671
"""
613672
Reads the Doxygen XML output and builds a snapshot of the C++ API.
@@ -628,12 +687,23 @@ def build_snapshot(xml_dir: str) -> Snapshot:
628687
doxygen_object = compound.parse(detail_file, silence=True)
629688

630689
for compound_object in doxygen_object.compounddef:
690+
# Check if this is an Objective-C interface by looking at the compound id
691+
# Doxygen reports ObjC interfaces as kind="class" but with id starting with "interface"
692+
is_objc_interface = (
693+
compound_object.kind == "class"
694+
and compound_object.id.startswith("interface")
695+
)
696+
631697
# classes and structs are represented by the same scope with a different kind
632698
if (
633699
compound_object.kind == "class"
634700
or compound_object.kind == "struct"
635701
or compound_object.kind == "union"
636702
):
703+
# Handle Objective-C interfaces separately
704+
if is_objc_interface:
705+
create_interface_scope(snapshot, compound_object)
706+
continue
637707
class_scope = (
638708
snapshot.create_struct_like(
639709
compound_object.compoundname, StructLikeScopeKind.Type.CLASS
@@ -781,6 +851,8 @@ def build_snapshot(xml_dir: str) -> Snapshot:
781851
pass
782852
elif compound_object.kind == "protocol":
783853
create_protocol_scope(snapshot, compound_object)
854+
elif compound_object.kind == "interface":
855+
create_interface_scope(snapshot, compound_object)
784856
else:
785857
print(f"Unknown compound kind: {compound_object.kind}")
786858

scripts/cxx-api/parser/scope.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,57 @@ def to_string(self, scope: Scope) -> str:
202202
return result
203203

204204

205+
class InterfaceScopeKind(ScopeKind):
206+
class Base:
207+
def __init__(
208+
self, name: str, protection: str, virtual: bool, refid: str
209+
) -> None:
210+
self.name: str = name
211+
self.protection: str = protection
212+
self.virtual: bool = virtual
213+
self.refid: str = refid
214+
215+
def __init__(self) -> None:
216+
super().__init__("interface")
217+
self.base_classes: [InterfaceScopeKind.Base] = []
218+
219+
def add_base(
220+
self, base: InterfaceScopeKind.Base | [InterfaceScopeKind.Base]
221+
) -> None:
222+
if isinstance(base, list):
223+
for b in base:
224+
self.base_classes.append(b)
225+
else:
226+
self.base_classes.append(base)
227+
228+
def to_string(self, scope: Scope) -> str:
229+
result = ""
230+
231+
bases = []
232+
for base in self.base_classes:
233+
base_text = [base.protection]
234+
if base.virtual:
235+
base_text.append("virtual")
236+
base_text.append(base.name)
237+
bases.append(" ".join(base_text))
238+
239+
inheritance_string = " : " + ", ".join(bases) if bases else ""
240+
241+
result += f"{self.name} {scope.get_qualified_name()}{inheritance_string} {{"
242+
243+
stringified_members = []
244+
for member in scope.get_members():
245+
stringified_members.append(member.to_string(2))
246+
stringified_members = natsorted(stringified_members)
247+
result += ("\n" if len(stringified_members) > 0 else "") + "\n".join(
248+
stringified_members
249+
)
250+
251+
result += "\n}"
252+
253+
return result
254+
255+
205256
class TemporaryScopeKind(ScopeKind):
206257
def __init__(self) -> None:
207258
super().__init__("temporary")

scripts/cxx-api/parser/snapshot.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .scope import (
99
EnumScopeKind,
10+
InterfaceScopeKind,
1011
NamespaceScopeKind,
1112
ProtocolScopeKind,
1213
Scope,
@@ -109,6 +110,30 @@ def create_protocol(self, qualified_name: str) -> Scope[ProtocolScopeKind]:
109110
current_scope.inner_scopes[scope_name] = new_scope
110111
return new_scope
111112

113+
def create_interface(self, qualified_name: str) -> Scope[InterfaceScopeKind]:
114+
"""
115+
Create an interface in the snapshot.
116+
"""
117+
path = parse_qualified_path(qualified_name)
118+
scope_path = path[0:-1]
119+
scope_name = path[-1]
120+
current_scope = self.ensure_scope(scope_path)
121+
122+
if scope_name in current_scope.inner_scopes:
123+
scope = current_scope.inner_scopes[scope_name]
124+
if scope.kind.name == "temporary":
125+
scope.kind = InterfaceScopeKind()
126+
else:
127+
raise RuntimeError(
128+
f"Identifier {scope_name} already exists in scope {current_scope.name}"
129+
)
130+
return scope
131+
else:
132+
new_scope = Scope(InterfaceScopeKind(), scope_name)
133+
new_scope.parent_scope = current_scope
134+
current_scope.inner_scopes[scope_name] = new_scope
135+
return new_scope
136+
112137
def create_enum(self, qualified_name: str) -> Scope[EnumScopeKind]:
113138
"""
114139
Create an enum in the snapshot.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
interface RCTEmptyInterface {
2+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@interface RCTEmptyInterface
9+
10+
@end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
interface RCTBaseInterface {
2+
public virtual void baseMethod();
3+
}
4+
5+
interface RCTChildInterface : public RCTBaseInterface {
6+
public virtual void childMethod();
7+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@interface RCTBaseInterface
9+
10+
- (void)baseMethod;
11+
12+
@end
13+
14+
@interface RCTChildInterface : RCTBaseInterface
15+
16+
- (void)childMethod;
17+
18+
@end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface RCTInterfaceWithMethod {
2+
public virtual NSString * getTitle();
3+
public virtual void doSomething();
4+
public virtual void processValue:withName:(int value, NSString * name);
5+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@interface RCTInterfaceWithMethod
9+
10+
- (void)doSomething;
11+
- (NSString *)getTitle;
12+
- (void)processValue:(int)value withName:(NSString *)name;
13+
14+
@end

0 commit comments

Comments
 (0)