From fb8e0c9279be5507c66cc26b571a62f39157650d Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Fri, 15 May 2026 10:43:55 +0200 Subject: [PATCH] fix: inherit base members from dependent template specializations When the base is a dependent template specialization (e.g., `base` in `template class derived : public base`), `BaseMembersFinalizer` unconditionally read `SpecializationName::specializationID`, which is only set for concrete `ClassTemplateSpecializationDecl`s. For dependent bases, it stays invalid, so the `MRDOCS_CHECK_OR_CONTINUE(baseID)` guard silently skipped the base and no members were inherited. The fix falls back to the primary template's ID (`Name::id`) when `specializationID` is invalid, so a class template derived from a dependent specialization now picks up the primary's members. Concrete cases like `derived : public base` still inherit from the implicit specialization as before. Closes issue #1176. --- .../Finalizers/BaseMembersFinalizer.cpp | 15 +- .../inherit-base-members/template-base.adoc | 236 ++++++++++ .../inherit-base-members/template-base.cpp | 29 ++ .../inherit-base-members/template-base.html | 320 ++++++++++++++ .../inherit-base-members/template-base.xml | 407 ++++++++++++++++++ .../inherit-base-members/template-base.yml | 1 + 6 files changed, 1007 insertions(+), 1 deletion(-) create mode 100644 test-files/golden-tests/config/inherit-base-members/template-base.adoc create mode 100644 test-files/golden-tests/config/inherit-base-members/template-base.cpp create mode 100644 test-files/golden-tests/config/inherit-base-members/template-base.html create mode 100644 test-files/golden-tests/config/inherit-base-members/template-base.xml create mode 100644 test-files/golden-tests/config/inherit-base-members/template-base.yml diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp index a87fb7a107..5e5be33e45 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -242,12 +242,25 @@ operator()(RecordSymbol& I) auto& baseNameType = baseI.Type->asNamed(); MRDOCS_ASSERT(!baseNameType.Name.valueless_after_move()); auto& baseName = baseNameType.Name->asName(); + // `baseName.id` is the primary template's ID. When the base + // names a concrete specialization (e.g. `base`) and + // `extract-implicit-specializations` is on, we prefer the + // implicit specialization's ID so inherited members carry + // the substituted types. For a dependent base such as + // `base` in `template class derived : public base`, + // `specializationID` stays invalid (no `ClassTemplate- + // SpecializationDecl` exists in the AST), so we fall back + // to the primary's ID and inherit its members with the + // primary's template parameter intact. SymbolID baseID = baseName.id; if (corpus_.config->extractImplicitSpecializations && baseName.isSpecialization()) { auto& baseSpec = baseName.asSpecialization(); - baseID = baseSpec.specializationID; + if (baseSpec.specializationID) + { + baseID = baseSpec.specializationID; + } } MRDOCS_CHECK_OR_CONTINUE(baseID); auto basePtr = corpus_.find(baseID); diff --git a/test-files/golden-tests/config/inherit-base-members/template-base.adoc b/test-files/golden-tests/config/inherit-base-members/template-base.adoc new file mode 100644 index 0000000000..f87845bd57 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/template-base.adoc @@ -0,0 +1,236 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols="1,4"] +|=== +| Name| Description +| link:#base-06[`base`] +| A class template base. +| link:#concrete_derived[`concrete_derived`] +| A non‐template that inherits from a concrete specialization. +| link:#derived[`derived`] +| A class template that inherits from `base<T>`. +|=== + +[#base-06] +== base + +A class template base. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<typename T> +class base; +---- + +=== Member Functions + +[cols="1,4"] +|=== +| Name| Description +| link:#base-06-method[`method`] +| A regular member function that should also be inherited. +| link:#base-06-operator_subs[`operator[]`] +| Indexing operator that should be inherited. +|=== + +=== Derived Classes + +[cols="1,4"] +|=== +|Name|Description + +| link:#concrete_derived[`concrete_derived`] +| A non‐template that inherits from a concrete specialization. +| link:#derived[`derived`] +| A class template that inherits from `base<T>`. +|=== + +[#base-06-method] +== link:#base-06[base]::method + +A regular member function that should also be inherited. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +method(); +---- + +[#base-06-operator_subs] +== link:#base-06[base]::operator[] + +Indexing operator that should be inherited. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +operator[](int n); +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *n* +| The right operand +|=== + +[#concrete_derived] +== concrete_derived + +A non‐template that inherits from a concrete specialization. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class concrete_derived + : public link:#base-06[base<int>] +---- + +=== Base Classes + +[cols="1,4"] +|=== +|Name|Description + +| `link:#base-06[base<int>]` +| A class template base. +|=== + +=== Member Functions + +[cols=1] +|=== +| Name +| link:#concrete_derived-method[`method`] +| link:#concrete_derived-operator_subs[`operator[]`] +|=== + +[#concrete_derived-method] +== link:#concrete_derived[concrete_derived]::method + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +method(); +---- + +[#concrete_derived-operator_subs] +== link:#concrete_derived[concrete_derived]::operator[] + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +operator[](int n); +---- + +[#derived] +== derived + +A class template that inherits from `base<T>`. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<typename T> +class derived + : public link:#base-06[base<T>] +---- + +=== Base Classes + +[cols="1,4"] +|=== +|Name|Description + +| `link:#base-06[base<T>]` +| A class template base. +|=== + +=== Member Functions + +[cols="1,4"] +|=== +| Name| Description +| link:#derived-method[`method`] +| A regular member function that should also be inherited. +| link:#derived-operator_subs[`operator[]`] +| Indexing operator that should be inherited. +|=== + +[#derived-method] +== link:#derived[derived]::method + +A regular member function that should also be inherited. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +method(); +---- + +[#derived-operator_subs] +== link:#derived[derived]::operator[] + +Indexing operator that should be inherited. + +=== Synopsis + +Declared in `<template‐base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +operator[](int n); +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *n* +| The right operand +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/inherit-base-members/template-base.cpp b/test-files/golden-tests/config/inherit-base-members/template-base.cpp new file mode 100644 index 0000000000..1afed5b3fa --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/template-base.cpp @@ -0,0 +1,29 @@ +// Reproduces issue #1176: members defined in a class template base do not +// appear in derived classes that inherit from a specialization of that base. +// +// Boost.Multi's `array_ref` derives from `subarray`, and `subarray`'s +// `operator[]` did not show up on `array_ref`'s documentation page, +// even with `inherit-base-members: copy-all`. + +/// A class template base. +template +class base +{ +public: + /// Indexing operator that should be inherited. + T& operator[](int n); + + /// A regular member function that should also be inherited. + T& method(); +}; + +/// A class template that inherits from `base`. +template +class derived + : public base +{}; + +/// A non-template that inherits from a concrete specialization. +class concrete_derived + : public base +{}; diff --git a/test-files/golden-tests/config/inherit-base-members/template-base.html b/test-files/golden-tests/config/inherit-base-members/template-base.html new file mode 100644 index 0000000000..867e08422a --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/template-base.html @@ -0,0 +1,320 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+

+Types

+ + + + + + + + + + + + +
NameDescription
base A class template base.
concrete_derived A non-template that inherits from a concrete specialization.
derived A class template that inherits from base<T>.
+ +
+
+
+

+base

+
+

A class template base.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
template<typename T>
+class base;
+
+

+Member Functions

+ + + + + + + + + + + +
NameDescription
method A regular member function that should also be inherited.
operator[] Indexing operator that should be inherited.
+ + + +
+

+Derived Classes

+ + + + + + + + + + + +
NameDescription
concrete_derived + A non-template that inherits from a concrete specialization.
derived + A class template that inherits from base<T>.
+
+
+
+
+

+base::method

+
+

A regular member function that should also be inherited.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
T&
+method();
+
+
+
+
+

+base::operator[]

+
+

Indexing operator that should be inherited.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
T&
+operator[](int n);
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
nThe right operand
+
+
+
+
+

+concrete_derived

+
+

A non-template that inherits from a concrete specialization.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
class concrete_derived
+    : public base<int>
+
+
+

+Base Classes

+ + + + + + + + + + +
NameDescription
base<int>A class template base.
+
+

+Member Functions

+ + + + + + + + + + + +
Name
method
operator[]
+ + + +
+
+
+

+concrete_derived::method

+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
int&
+method();
+
+
+
+
+

+concrete_derived::operator[]

+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
int&
+operator[](int n);
+
+
+
+
+

+derived

+
+

A class template that inherits from base<T>.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
template<typename T>
+class derived
+    : public base<T>
+
+
+

+Base Classes

+ + + + + + + + + + +
NameDescription
base<T>A class template base.
+
+

+Member Functions

+ + + + + + + + + + + +
NameDescription
method A regular member function that should also be inherited.
operator[] Indexing operator that should be inherited.
+ + + +
+
+
+

+derived::method

+
+

A regular member function that should also be inherited.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
T&
+method();
+
+
+
+
+

+derived::operator[]

+
+

Indexing operator that should be inherited.

+
+
+
+

+Synopsis

+
+Declared in <template-base.cpp>
+
T&
+operator[](int n);
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
nThe right operand
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/template-base.xml b/test-files/golden-tests/config/inherit-base-members/template-base.xml new file mode 100644 index 0000000000..8d1d4b0835 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/template-base.xml @@ -0,0 +1,407 @@ + + + + + + namespace + //////////////////////////8= + regular + + gSa0yJZ5wX/+n5uAzlRPKCOgQ4Q= + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + qSoIJ38D4p+grUCWP6X5kItp3a8= + VmATJ/H4380dpA0mtYd7uf/hcZA= + + + + base + + + template-base.cpp + template-base.cpp + 9 + 1 + 1 + + + record + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + regular + //////////////////////////8= + + + brief + + text + A class template base. + + + + class + + qSoIJ38D4p+grUCWP6X5kItp3a8= + VmATJ/H4380dpA0mtYd7uf/hcZA= + + + POYVSjIb9yYJwMJ0idlrE7qVb8s= + Jykv51AJ/2ySE5tqs91b5BH4PR0= + + + + + + + + + method + + + template-base.cpp + template-base.cpp + 17 + 5 + 1 + + + function + POYVSjIb9yYJwMJ0idlrE7qVb8s= + regular + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + + + brief + + text + A regular member function that should also be inherited. + + + + + + + identifier + T + + + + normal + 1 + + + operator[] + + + template-base.cpp + template-base.cpp + 14 + 5 + 1 + + + function + Jykv51AJ/2ySE5tqs91b5BH4PR0= + regular + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + + + brief + + text + Indexing operator that should be inherited. + + + + param + + text + The right operand + + n + + + + + + identifier + T + + + + + + + identifier + int + + + n + + normal + 1 + + + concrete_derived + + + template-base.cpp + template-base.cpp + 27 + 1 + 1 + + + record + qSoIJ38D4p+grUCWP6X5kItp3a8= + regular + //////////////////////////8= + + + brief + + text + A non-template that inherits from a concrete specialization. + + + + class + + + + specialization + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + base + + + + + + 9X0U5wi8aI04GBZwYXOMit+BPNs= + fJuJ+E/X6B4WdSCqYQX0onTsvhs= + + + + + + + + + method + + + template-base.cpp + template-base.cpp + 17 + 5 + + + function + 9X0U5wi8aI04GBZwYXOMit+BPNs= + regular + 1 + qSoIJ38D4p+grUCWP6X5kItp3a8= + + + + identifier + int + + + + normal + 1 + + + operator[] + + + template-base.cpp + template-base.cpp + 14 + 5 + + + function + fJuJ+E/X6B4WdSCqYQX0onTsvhs= + regular + 1 + qSoIJ38D4p+grUCWP6X5kItp3a8= + + + + identifier + int + + + + + + + identifier + int + + + n + + normal + 1 + + + derived + + + template-base.cpp + template-base.cpp + 21 + 1 + 1 + + + record + VmATJ/H4380dpA0mtYd7uf/hcZA= + regular + //////////////////////////8= + + + brief + + text + A class template that inherits from + + + code + + text + base<T> + + + + text + . + + + + class + + + + + specialization + ZLNgZaGs9JV18Yo8wi3pzzp4vfE= + base + + + + + + w8SzajsNCB5ck0YpsMvHduC9g1E= + vt7li1a/kZQyCXxHvrdak2VBJQw= + + + + + + + + + method + + + template-base.cpp + template-base.cpp + 17 + 5 + 1 + + + function + w8SzajsNCB5ck0YpsMvHduC9g1E= + regular + 1 + VmATJ/H4380dpA0mtYd7uf/hcZA= + + + brief + + text + A regular member function that should also be inherited. + + + + + + + identifier + T + + + + normal + 1 + + + operator[] + + + template-base.cpp + template-base.cpp + 14 + 5 + 1 + + + function + vt7li1a/kZQyCXxHvrdak2VBJQw= + regular + 1 + VmATJ/H4380dpA0mtYd7uf/hcZA= + + + brief + + text + Indexing operator that should be inherited. + + + + param + + text + The right operand + + n + + + + + + identifier + T + + + + + + + identifier + int + + + n + + normal + 1 + + diff --git a/test-files/golden-tests/config/inherit-base-members/template-base.yml b/test-files/golden-tests/config/inherit-base-members/template-base.yml new file mode 100644 index 0000000000..5ffbf69b18 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/template-base.yml @@ -0,0 +1 @@ +inherit-base-members: copy-all