Skip to content

Commit 6ea9236

Browse files
j-piaseckimeta-codesync[bot]
authored andcommitted
Handle brace initializers (#55650)
Summary: Pull Request resolved: #55650 Changelog: [Internal] Updates the C++ snapshot generation script to correctly handle brace-initialized values. It also adds `STORE_INITIALIZERS_IN_SNAPSHOT` which can be used to enable/disable presence of initializers in the API snapshot. Reviewed By: cortinico Differential Revision: D93845131 fbshipit-source-id: 06872e705305ced0c46779f4a97c674f26417ff5
1 parent de87d99 commit 6ea9236

File tree

13 files changed

+192
-18
lines changed

13 files changed

+192
-18
lines changed

scripts/cxx-api/parser/main.py

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525

2626

2727
def resolve_ref_text_name(type_def: compound.refTextType) -> str:
28+
"""
29+
Resolve the full text content of a refTextType, including all text
30+
fragments and ref elements.
31+
"""
2832
if hasattr(type_def, "content_") and type_def.content_:
2933
name = ""
3034
for part in type_def.content_:
@@ -45,26 +49,74 @@ def resolve_ref_text_name(type_def: compound.refTextType) -> str:
4549
return type_def.get_valueOf_()
4650

4751

48-
def resolve_linked_text_name(type_def: compound.linkedTextType) -> str:
52+
def extract_namespace_from_refid(refid: str) -> str:
53+
"""Extract the namespace prefix from a doxygen refid.
54+
e.g. 'namespacefacebook_1_1yoga_1a...' -> 'facebook::yoga'
55+
"""
56+
for prefix in ("namespace", "struct", "class", "union"):
57+
if refid.startswith(prefix):
58+
compound_part = refid[len(prefix) :]
59+
idx = compound_part.find("_1a")
60+
if idx != -1:
61+
compound_part = compound_part[:idx]
62+
return compound_part.replace("_1_1", "::")
63+
return ""
64+
65+
66+
def resolve_linked_text_name(type_def: compound.linkedTextType) -> (str, bool):
67+
"""
68+
Resolve the full text content of a linkedTextType, including all text
69+
fragments and ref elements.
70+
"""
4971
name = ""
72+
in_string = False
5073

5174
for part in type_def.content_:
5275
if part.category == 1: # MixedContainer.CategoryText
76+
in_string = part.value.count('"') % 2 != in_string
5377
name += part.value
5478
elif part.category == 3: # MixedContainer.CategoryComplex (ref element)
55-
# For ref elements, get the text content
79+
# For ref elements, get the text content and fully qualify using refid
80+
text = ""
5681
if hasattr(part.value, "get_valueOf_"):
57-
name += part.value.get_valueOf_()
82+
text = part.value.get_valueOf_()
5883
elif hasattr(part.value, "valueOf_"):
59-
name += part.value.valueOf_
84+
text = part.value.valueOf_
6085
else:
61-
name += str(part.value)
62-
63-
# literal initializers keep "=" sign
86+
text = str(part.value)
87+
88+
# Don't resolve refs inside string literals - doxygen may
89+
# incorrectly treat symbols in strings as references
90+
refid = getattr(part.value, "refid", None)
91+
if refid and not in_string:
92+
ns = extract_namespace_from_refid(refid)
93+
if ns and not text.startswith(ns):
94+
# The text may already start with a trailing portion of
95+
# the namespace. For example ns="facebook::react::HighResDuration"
96+
# and text="HighResDuration::zero". We need to find the
97+
# longest suffix of ns that is a prefix of text (on a "::"
98+
# boundary) and only prepend the missing part.
99+
ns_parts = ns.split("::")
100+
prepend = ns
101+
for i in range(1, len(ns_parts)):
102+
suffix = "::".join(ns_parts[i:])
103+
if text.startswith(suffix + "::") or text == suffix:
104+
prepend = "::".join(ns_parts[:i])
105+
break
106+
text = prepend + "::" + text
107+
108+
name += text
109+
110+
is_brace_initializer = False
64111
if name.startswith("="):
112+
# Detect assignment initializers: = value
65113
name = name[1:]
114+
elif name.startswith("{") and name.endswith("}"):
115+
# Detect brace initializers: {value}
116+
is_brace_initializer = True
117+
name = name[1:-1].strip()
66118

67-
return name.strip()
119+
return (name.strip(), is_brace_initializer)
68120

69121

70122
def resolve_linked_text_full(type_def: compound.linkedTextType) -> str:
@@ -198,8 +250,11 @@ def get_variable_member(
198250
if is_const:
199251
variable_type = variable_type[5:].strip()
200252

253+
is_brace_initializer = False
201254
if member_def.initializer is not None:
202-
variable_value = resolve_linked_text_name(member_def.initializer)
255+
(variable_value, is_brace_initializer) = resolve_linked_text_name(
256+
member_def.initializer
257+
)
203258

204259
return VariableMember(
205260
variable_name,
@@ -212,6 +267,7 @@ def get_variable_member(
212267
variable_value,
213268
variable_definition,
214269
variable_argstring,
270+
is_brace_initializer,
215271
)
216272

217273

@@ -370,7 +426,7 @@ def create_enum_scope(snapshot: Snapshot, enum_def: compound.EnumdefType):
370426
value_value = None
371427

372428
if enum_value_def.initializer is not None:
373-
value_value = resolve_linked_text_name(enum_value_def.initializer)
429+
(value_value, _) = resolve_linked_text_name(enum_value_def.initializer)
374430

375431
scope.add_member(
376432
EnumMember(

scripts/cxx-api/parser/member.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
if TYPE_CHECKING:
2626
from .scope import Scope
2727

28+
STORE_INITIALIZERS_IN_SNAPSHOT = False
29+
2830

2931
class MemberKind(IntEnum):
3032
"""
@@ -94,7 +96,7 @@ def to_string(
9496
) -> str:
9597
name = self._get_qualified_name(qualification)
9698

97-
if self.value is None:
99+
if not STORE_INITIALIZERS_IN_SNAPSHOT or self.value is None:
98100
return " " * indent + f"{name}"
99101

100102
return " " * indent + f"{name} = {self.value}"
@@ -113,6 +115,7 @@ def __init__(
113115
value: str | None,
114116
definition: str,
115117
argstring: str | None = None,
118+
is_brace_initializer: bool = False,
116119
) -> None:
117120
super().__init__(name, visibility)
118121
self.type: str = type
@@ -121,6 +124,7 @@ def __init__(
121124
self.is_static: bool = is_static
122125
self.is_constexpr: bool = is_constexpr
123126
self.is_mutable: bool = is_mutable
127+
self.is_brace_initializer: bool = is_brace_initializer
124128
self.definition: str = definition
125129
self.argstring: str | None = argstring
126130
self._fp_arguments: list[Argument] = (
@@ -180,8 +184,11 @@ def to_string(
180184
else:
181185
result += f"{format_parsed_type(self._parsed_type)} {name}"
182186

183-
if self.value is not None and (self.is_const or self.is_constexpr):
184-
result += f" = {self.value}"
187+
if STORE_INITIALIZERS_IN_SNAPSHOT and self.value is not None:
188+
if self.is_brace_initializer:
189+
result += f"{{{self.value}}}"
190+
else:
191+
result += f" = {self.value}"
185192

186193
result += ";"
187194

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
struct test::State {
2+
public test::State::Mode mode;
3+
}
4+
5+
enum test::State::Mode {
6+
Auto,
7+
Disabled,
8+
Manual,
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
#pragma once
9+
10+
namespace test {
11+
12+
struct State {
13+
enum class Mode { Auto, Manual, Disabled };
14+
Mode mode{Mode::Auto};
15+
};
16+
17+
} // namespace test
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class test::Base {
2+
public bool disabled;
3+
public bool selected;
4+
public int count;
5+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
#pragma once
9+
10+
namespace test {
11+
12+
class Base {
13+
public:
14+
bool disabled{false};
15+
bool selected{true};
16+
int count{0};
17+
};
18+
19+
} // namespace test
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
struct test::Config {
2+
public std::optional< bool > expanded;
3+
public std::optional< std::string > label;
4+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
#pragma once
9+
10+
#include <optional>
11+
#include <string>
12+
13+
namespace test {
14+
15+
struct Config {
16+
std::optional<bool> expanded{std::nullopt};
17+
std::optional<std::string> label{};
18+
};
19+
20+
} // namespace test
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class test::Clss {
2-
public constexpr int value1 = 1;
3-
public constexpr int value2 = 1;
2+
public constexpr int value1;
3+
public constexpr int value2;
44
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class test::Base {
2-
public const int value = 1;
2+
public const int value;
33
}

0 commit comments

Comments
 (0)