Skip to content

Commit 1430898

Browse files
j-piaseckimeta-codesync[bot]
authored andcommitted
Split member definitions into smaller files (#56072)
Summary: Pull Request resolved: #56072 Changelog: [Internal] Splits `member.py` into multiple smaller files, each containing single member definition. Differential Revision: D96282653
1 parent 16ffd31 commit 1430898

File tree

10 files changed

+666
-549
lines changed

10 files changed

+666
-549
lines changed

scripts/cxx-api/parser/member.py

Lines changed: 0 additions & 549 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from .base import Member, MemberKind, STORE_INITIALIZERS_IN_SNAPSHOT
7+
from .concept_member import ConceptMember
8+
from .enum_member import EnumMember
9+
from .friend_member import FriendMember
10+
from .function_member import FunctionMember
11+
from .property_member import PropertyMember
12+
from .typedef_member import TypedefMember
13+
from .variable_member import VariableMember
14+
15+
__all__ = [
16+
"ConceptMember",
17+
"EnumMember",
18+
"FriendMember",
19+
"FunctionMember",
20+
"Member",
21+
"MemberKind",
22+
"PropertyMember",
23+
"STORE_INITIALIZERS_IN_SNAPSHOT",
24+
"TypedefMember",
25+
"VariableMember",
26+
]
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from __future__ import annotations
7+
8+
from abc import ABC, abstractmethod
9+
from enum import IntEnum
10+
from typing import TYPE_CHECKING
11+
12+
from ..template import Template, TemplateList
13+
14+
if TYPE_CHECKING:
15+
from ..scope import Scope
16+
17+
STORE_INITIALIZERS_IN_SNAPSHOT = False
18+
19+
20+
class MemberKind(IntEnum):
21+
"""
22+
Classification of member kinds for grouping in output.
23+
The order here determines the output order within namespace scopes.
24+
"""
25+
26+
CONSTANT = 0
27+
TYPE_ALIAS = 1
28+
CONCEPT = 2
29+
FUNCTION = 3
30+
OPERATOR = 4
31+
VARIABLE = 5
32+
FRIEND = 6
33+
34+
35+
class Member(ABC):
36+
def __init__(self, name: str, visibility: str) -> None:
37+
self.name: str = name
38+
self.visibility: str = visibility
39+
self.template_list: TemplateList | None = None
40+
41+
@property
42+
@abstractmethod
43+
def member_kind(self) -> MemberKind:
44+
pass
45+
46+
@abstractmethod
47+
def to_string(
48+
self,
49+
indent: int = 0,
50+
qualification: str | None = None,
51+
hide_visibility: bool = False,
52+
) -> str:
53+
pass
54+
55+
def close(self, scope: Scope):
56+
pass
57+
58+
def _get_qualified_name(self, qualification: str | None):
59+
return f"{qualification}::{self.name}" if qualification else self.name
60+
61+
def add_template(self, template: Template | [Template]) -> None:
62+
if template and self.template_list is None:
63+
self.template_list = TemplateList()
64+
65+
if isinstance(template, list):
66+
for t in template:
67+
self.template_list.add(t)
68+
else:
69+
self.template_list.add(template)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from __future__ import annotations
7+
8+
from typing import TYPE_CHECKING
9+
10+
from .base import Member, MemberKind
11+
12+
if TYPE_CHECKING:
13+
from ..scope import Scope
14+
15+
16+
class ConceptMember(Member):
17+
def __init__(
18+
self,
19+
name: str,
20+
constraint: str,
21+
) -> None:
22+
super().__init__(name, "public")
23+
self.constraint: str = self._normalize_constraint(constraint)
24+
25+
@property
26+
def member_kind(self) -> MemberKind:
27+
return MemberKind.CONCEPT
28+
29+
@staticmethod
30+
def _normalize_constraint(constraint: str) -> str:
31+
"""
32+
Normalize the whitespace in a concept constraint expression.
33+
34+
Doxygen preserves original source indentation, which becomes
35+
inconsistent when we flatten namespaces and use qualified names.
36+
This method normalizes the indentation by dedenting all lines
37+
to the minimum non-empty indentation level.
38+
"""
39+
if not constraint:
40+
return constraint
41+
42+
lines = constraint.split("\n")
43+
if len(lines) <= 1:
44+
return constraint.strip()
45+
46+
# Find minimum indentation (excluding the first line and empty lines)
47+
min_indent = float("inf")
48+
for line in lines[1:]:
49+
stripped = line.lstrip()
50+
if stripped: # Skip empty lines
51+
indent = len(line) - len(stripped)
52+
min_indent = min(min_indent, indent)
53+
54+
if min_indent == float("inf"):
55+
min_indent = 0
56+
57+
# Dedent all lines by the minimum indentation
58+
result_lines = [lines[0].strip()]
59+
for line in lines[1:]:
60+
if line.strip(): # Non-empty line
61+
# Remove the minimum indentation to normalize
62+
dedented = (
63+
line[int(min_indent) :]
64+
if len(line) >= min_indent
65+
else line.lstrip()
66+
)
67+
result_lines.append(dedented.rstrip())
68+
else:
69+
result_lines.append("")
70+
71+
# Check if no line is indented
72+
if all(not line.startswith(" ") for line in result_lines):
73+
# Re-indent all lines but the first by 2 spaces
74+
not_indented = result_lines
75+
result_lines = [not_indented[0]]
76+
for line in not_indented[1:]:
77+
if line.strip(): # Non-empty line
78+
result_lines.append(" " + line)
79+
else:
80+
result_lines.append("")
81+
82+
return "\n".join(result_lines)
83+
84+
def close(self, scope: Scope):
85+
# TODO: handle unqualified references
86+
pass
87+
88+
def to_string(
89+
self,
90+
indent: int = 0,
91+
qualification: str | None = None,
92+
hide_visibility: bool = False,
93+
) -> str:
94+
name = self._get_qualified_name(qualification)
95+
result = ""
96+
97+
if self.template_list is not None:
98+
result += " " * indent + self.template_list.to_string() + "\n"
99+
100+
result += " " * indent + f"concept {name} = {self.constraint};"
101+
102+
return result
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from __future__ import annotations
7+
8+
from .base import Member, MemberKind, STORE_INITIALIZERS_IN_SNAPSHOT
9+
10+
11+
class EnumMember(Member):
12+
def __init__(self, name: str, value: str | None) -> None:
13+
super().__init__(name, "public")
14+
self.value: str | None = value
15+
16+
@property
17+
def member_kind(self) -> MemberKind:
18+
return MemberKind.CONSTANT
19+
20+
def to_string(
21+
self,
22+
indent: int = 0,
23+
qualification: str | None = None,
24+
hide_visibility: bool = False,
25+
) -> str:
26+
name = self._get_qualified_name(qualification)
27+
28+
if not STORE_INITIALIZERS_IN_SNAPSHOT or self.value is None:
29+
return " " * indent + f"{name}"
30+
31+
return " " * indent + f"{name} = {self.value}"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from __future__ import annotations
7+
8+
from .base import Member, MemberKind
9+
10+
11+
class FriendMember(Member):
12+
def __init__(self, name: str, visibility: str = "public") -> None:
13+
super().__init__(name, visibility)
14+
15+
@property
16+
def member_kind(self) -> MemberKind:
17+
return MemberKind.FRIEND
18+
19+
def to_string(
20+
self,
21+
indent: int = 0,
22+
qualification: str | None = None,
23+
hide_visibility: bool = False,
24+
) -> str:
25+
name = self._get_qualified_name(qualification)
26+
result = " " * indent
27+
if not hide_visibility:
28+
result += self.visibility + " "
29+
result += f"friend {name};"
30+
return result
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from __future__ import annotations
7+
8+
from typing import TYPE_CHECKING
9+
10+
from ..utils import (
11+
Argument,
12+
format_arguments,
13+
parse_arg_string,
14+
qualify_arguments,
15+
qualify_template_args_only,
16+
qualify_type_str,
17+
)
18+
from .base import Member, MemberKind
19+
20+
if TYPE_CHECKING:
21+
from ..scope import Scope
22+
23+
24+
class FunctionMember(Member):
25+
def __init__(
26+
self,
27+
name: str,
28+
type: str,
29+
visibility: str,
30+
arg_string: str,
31+
is_virtual: bool,
32+
is_pure_virtual: bool,
33+
is_static: bool,
34+
doxygen_params: list[Argument] | None = None,
35+
is_constexpr: bool = False,
36+
) -> None:
37+
super().__init__(name, visibility)
38+
self.type: str = type
39+
self.is_virtual: bool = is_virtual
40+
self.is_static: bool = is_static
41+
self.is_constexpr: bool = is_constexpr
42+
parsed_arguments, self.modifiers = parse_arg_string(arg_string)
43+
self.arguments = (
44+
doxygen_params if doxygen_params is not None else parsed_arguments
45+
)
46+
47+
# Doxygen signals pure-virtual via the virt attribute, but the arg string
48+
# may not contain "= 0" (e.g. trailing return type syntax), so the
49+
# modifiers parsed from the arg string may miss it. Propagate the flag.
50+
if is_pure_virtual:
51+
self.modifiers.is_pure_virtual = True
52+
53+
self.is_const = self.modifiers.is_const
54+
self.is_override = self.modifiers.is_override
55+
56+
@property
57+
def member_kind(self) -> MemberKind:
58+
if self.name.startswith("operator"):
59+
return MemberKind.OPERATOR
60+
return MemberKind.FUNCTION
61+
62+
def close(self, scope: Scope):
63+
self.type = qualify_type_str(self.type, scope)
64+
self.arguments = qualify_arguments(self.arguments, scope)
65+
# Qualify template arguments in function name for explicit specializations
66+
# e.g., "convert<MyType>" -> "convert<ns::MyType>"
67+
if "<" in self.name:
68+
self.name = qualify_template_args_only(self.name, scope)
69+
70+
def to_string(
71+
self,
72+
indent: int = 0,
73+
qualification: str | None = None,
74+
hide_visibility: bool = False,
75+
) -> str:
76+
name = self._get_qualified_name(qualification)
77+
result = ""
78+
79+
if self.template_list is not None:
80+
result += " " * indent + self.template_list.to_string() + "\n"
81+
82+
result += " " * indent
83+
84+
if not hide_visibility:
85+
result += self.visibility + " "
86+
87+
if self.is_virtual:
88+
result += "virtual "
89+
90+
if self.is_static:
91+
result += "static "
92+
93+
if self.is_constexpr:
94+
result += "constexpr "
95+
96+
if self.type:
97+
result += f"{self.type} "
98+
99+
result += f"{name}({format_arguments(self.arguments)})"
100+
101+
if self.modifiers.is_const:
102+
result += " const"
103+
104+
if self.modifiers.is_noexcept:
105+
if self.modifiers.noexcept_expr:
106+
result += f" noexcept({self.modifiers.noexcept_expr})"
107+
else:
108+
result += " noexcept"
109+
110+
if self.modifiers.is_override:
111+
result += " override"
112+
113+
if self.modifiers.is_final:
114+
result += " final"
115+
116+
if self.modifiers.is_pure_virtual:
117+
result += " = 0"
118+
elif self.modifiers.is_default:
119+
result += " = default"
120+
elif self.modifiers.is_delete:
121+
result += " = delete"
122+
123+
result += ";"
124+
return result

0 commit comments

Comments
 (0)