Skip to content

Commit 3d68227

Browse files
j-piaseckimeta-codesync[bot]
authored andcommitted
Extract class specializations from name (#56075)
Summary: Pull Request resolved: #56075 Changelog: [Internal] Updates the parser to extract the template specializations from name string in order to avoid repeated operations on the name string. Differential Revision: D96303732
1 parent a33565a commit 3d68227

File tree

6 files changed

+59
-14
lines changed

6 files changed

+59
-14
lines changed

scripts/cxx-api/parser/builders.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
normalize_pointer_spacing,
3939
parse_qualified_path,
4040
resolve_linked_text_name,
41+
split_specialization,
4142
)
4243
from .utils.argument_parsing import _find_matching_angle, _split_arguments
4344

@@ -124,8 +125,7 @@ def _fix_inherited_constructor_name(
124125
return
125126

126127
class_unqualified_name = parse_qualified_path(compound_name)[-1]
127-
# Strip template args for comparison
128-
class_base_name = class_unqualified_name.split("<")[0]
128+
class_base_name, _ = split_specialization(class_unqualified_name)
129129

130130
if func_member.name != class_base_name:
131131
func_member.name = class_unqualified_name

scripts/cxx-api/parser/scope/scope.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from natsort import natsorted
1111

1212
from ..member import FriendMember, Member, TypedefMember
13-
from ..utils import parse_qualified_path, qualify_template_args_only
13+
from ..utils import parse_qualified_path, qualify_type_str
1414
from .base_scope_kind import _natsort_key, ScopeKindT
1515
from .enum_scope_kind import EnumScopeKind
1616
from .namespace_scope_kind import NamespaceScopeKind
@@ -35,10 +35,14 @@ def get_qualified_name(self) -> str:
3535
current_scope = self
3636
while current_scope is not None:
3737
if current_scope.name is not None:
38-
# Qualify template arguments in the scope name if it has any
3938
name = current_scope.name
40-
if "<" in name and current_scope.parent_scope is not None:
41-
name = qualify_template_args_only(name, current_scope.parent_scope)
39+
if (
40+
isinstance(current_scope.kind, StructLikeScopeKind)
41+
and current_scope.kind.specialization_args is not None
42+
):
43+
name = (
44+
f"{name}<{', '.join(current_scope.kind.specialization_args)}>"
45+
)
4246
path.append(name)
4347
current_scope = current_scope.parent_scope
4448
path.reverse()
@@ -174,6 +178,18 @@ def close(self) -> None:
174178
"""
175179
Close the scope by setting the kind of all temporary scopes.
176180
"""
181+
# Qualify specialization args early so that members and inner scopes
182+
# see the fully-qualified name when they call get_qualified_name().
183+
if (
184+
isinstance(self.kind, StructLikeScopeKind)
185+
and self.kind.specialization_args is not None
186+
and self.parent_scope is not None
187+
):
188+
self.kind.specialization_args = [
189+
qualify_type_str(arg, self.parent_scope)
190+
for arg in self.kind.specialization_args
191+
]
192+
177193
for typedef in self._private_typedefs.values():
178194
typedef.close(self)
179195

scripts/cxx-api/parser/scope/struct_like_scope_kind.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ class Type(Enum):
2525
STRUCT = "struct"
2626
UNION = "union"
2727

28-
def __init__(self, type: Type) -> None:
28+
def __init__(
29+
self, type: Type, specialization_args: list[str] | None = None
30+
) -> None:
2931
ScopeKind.__init__(self, type.value)
3032
Extendable.__init__(self)
3133

3234
self.template_list: TemplateList | None = None
35+
self.specialization_args = specialization_args
3336

3437
def add_template(self, template: Template | [Template]) -> None:
3538
if template and self.template_list is None:

scripts/cxx-api/parser/snapshot.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
StructLikeScopeKind,
1616
TemporaryScopeKind,
1717
)
18-
from .utils import parse_qualified_path
18+
from .utils import parse_qualified_path, split_specialization
1919

2020

2121
class Snapshot:
@@ -46,19 +46,26 @@ def create_struct_like(
4646
scope_name = path[-1]
4747
current_scope = self.ensure_scope(scope_path)
4848

49-
if scope_name in current_scope.inner_scopes:
50-
scope = current_scope.inner_scopes[scope_name]
49+
base_name, specialization_args = split_specialization(scope_name)
50+
51+
# Use the full name (including specialization) as the dict key so that
52+
# base templates and their specializations are distinct entries.
53+
scope_key = scope_name
54+
55+
if scope_key in current_scope.inner_scopes:
56+
scope = current_scope.inner_scopes[scope_key]
5157
if scope.kind.name == "temporary":
52-
scope.kind = StructLikeScopeKind(type)
58+
scope.kind = StructLikeScopeKind(type, specialization_args)
59+
scope.name = base_name
5360
else:
5461
raise RuntimeError(
55-
f"Identifier {scope_name} already exists in scope {current_scope.name}"
62+
f"Identifier {scope_key} already exists in scope {current_scope.name}"
5663
)
5764
return scope
5865
else:
59-
new_scope = Scope(StructLikeScopeKind(type), scope_name)
66+
new_scope = Scope(StructLikeScopeKind(type, specialization_args), base_name)
6067
new_scope.parent_scope = current_scope
61-
current_scope.inner_scopes[scope_name] = new_scope
68+
current_scope.inner_scopes[scope_key] = new_scope
6269
return new_scope
6370

6471
def create_or_get_namespace(self, qualified_name: str) -> Scope[NamespaceScopeKind]:

scripts/cxx-api/parser/utils/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
parse_arg_string,
1313
parse_function_pointer_argstring,
1414
parse_type_with_argstrings,
15+
split_specialization,
1516
)
1617
from .qualified_path import parse_qualified_path
1718
from .text_resolution import (
@@ -37,6 +38,7 @@
3738
"FunctionModifiers",
3839
"InitializerType",
3940
"normalize_angle_brackets",
41+
"normalize_pointer_spacing",
4042
"parse_arg_string",
4143
"parse_function_pointer_argstring",
4244
"parse_qualified_path",
@@ -46,4 +48,5 @@
4648
"qualify_template_args_only",
4749
"qualify_type_str",
4850
"resolve_linked_text_name",
51+
"split_specialization",
4952
]

scripts/cxx-api/parser/utils/argument_parsing.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,22 @@ def _split_arguments(args_str: str) -> list[str]:
166166
return [arg for arg in result if arg]
167167

168168

169+
def split_specialization(name: str) -> tuple[str, list[str] | None]:
170+
"""Split a potentially specialized name into base name and specialization args.
171+
172+
Examples:
173+
"Foo" -> ("Foo", None)
174+
"Foo<int>" -> ("Foo", ["int"])
175+
"Foo<int, float>" -> ("Foo", ["int", "float"])
176+
"Foo<Bar<int>>" -> ("Foo", ["Bar<int>"])
177+
"""
178+
angle_start = name.find("<")
179+
if angle_start == -1 or not name.endswith(">"):
180+
return (name, None)
181+
args = _split_arguments(name[angle_start + 1 : -1])
182+
return (name[:angle_start], args if args else None)
183+
184+
169185
def _prefix_is_all_qualifiers(prefix: str) -> bool:
170186
"""Check if all tokens in the prefix are type qualifiers/specifiers.
171187

0 commit comments

Comments
 (0)