Files
Dawid Małecki d920d109cf Fix Doxygen merging base classes from primary templates into specializations (#56239)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/56239

Doxygen incorrectly merges base classes from primary templates into their partial specializations. In C++, a specialization's inheritance list completely replaces the primary template's, but Doxygen combines both into a single basecompoundref list.

- **Contradictory type traits** — is_optional<std::optional<T>> and is_variant_of_data_types<std::variant<Ts...>> showed inheritance from both std::false_type (primary) and std::true_type (specialization)

- **Duplicate base classes** — Converter<jsi::Object> listed ConverterBase<jsi::Object> twice (once from the primary template after substitution, once from the specialization)

This diff fixes both issues with two complementary mechanisms:

- **Dedup-by-name (Extendable._deduplicate_base_classes)**: removes exact duplicate base classes, keeping the last occurrence. Handles cases where Doxygen's template argument substitution produces identical names.
- **Primary template base subtraction (StructLikeScopeKind._remove_merged_primary_bases)**: for partial specializations, looks up the primary template among sibling scopes and performs count-based subtraction of its bases. Count-based (rather than set-based) subtraction correctly preserves bases that a specialization explicitly re-inherits from the same class as the primary.

Changelog:
[Internal]

Reviewed By: cipolleschi

Differential Revision: D98291360

fbshipit-source-id: 82b34969b13c7df8babf2c5efc222b29979d6361
2026-03-31 08:32:57 -07:00

60 lines
2.0 KiB
Python

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import annotations
class Extendable:
class Base:
def __init__(
self, name: str, protection: str, virtual: bool, refid: str
) -> None:
self.name: str = name
self.protection: str = protection
self.virtual: bool = virtual
self.refid: str = refid
def __init__(self) -> None:
self.base_classes = []
def add_base(self, base: Base | list[Base]) -> None:
if isinstance(base, list):
for b in base:
self.base_classes.append(b)
else:
self.base_classes.append(base)
self._deduplicate_base_classes()
def _deduplicate_base_classes(self) -> None:
"""Remove duplicate base classes.
Doxygen sometimes reports the same base class multiple times (e.g.
when template argument substitution produces identical names for
a primary template and its specialization). This keeps only the
last occurrence of each name.
"""
seen: dict[str, int] = {}
for i, base in enumerate(self.base_classes):
seen[base.name] = i
self.base_classes = [self.base_classes[i] for i in sorted(seen.values())]
def qualify_base_classes(self, scope) -> None:
"""Qualify base class names and their template arguments."""
from ..utils import qualify_type_str
for base in self.base_classes:
base.name = qualify_type_str(base.name, scope)
def get_inheritance_string(self) -> str:
bases = []
for base in self.base_classes:
base_text = [base.protection]
if base.virtual:
base_text.append("virtual")
base_text.append(base.name)
bases.append(" ".join(base_text))
return (" : " + ", ".join(bases)) if bases else ""