diff --git a/include/mrdocs/Metadata/Info/Function.hpp b/include/mrdocs/Metadata/Info/Function.hpp index 2cbf08bcc2..56489b23eb 100644 --- a/include/mrdocs/Metadata/Info/Function.hpp +++ b/include/mrdocs/Metadata/Info/Function.hpp @@ -267,6 +267,12 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } +/** Determine if one function would override the other + */ +MRDOCS_DECL +bool +overrides(FunctionInfo const& base, FunctionInfo const& derived); + } // clang::mrdocs #endif diff --git a/include/mrdocs/Support/Algorithm.hpp b/include/mrdocs/Support/Algorithm.hpp index 74d3cee9d7..ee8b316512 100644 --- a/include/mrdocs/Support/Algorithm.hpp +++ b/include/mrdocs/Support/Algorithm.hpp @@ -163,6 +163,7 @@ contains_n_any(Range const& range, std::initializer_list const& els, std::si } /** Find the last element in a range that matches an element in the specified range. + @param range The range to search. @param els The elements to search for. @return An iterator to the last element found, or std::ranges::end(range) if not found. diff --git a/src/lib/Gen/hbs/SinglePageVisitor.cpp b/src/lib/Gen/hbs/SinglePageVisitor.cpp index e53d8ee75b..d1f5d2bf1e 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.cpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.cpp @@ -25,18 +25,17 @@ operator()(T const& I) { ex_.async([this, &I, symbolIdx = numSymbols_++](Builder& builder) { - - // Output to an independent string first (async), then write to - // the shared stream (sync) - std::stringstream ss; - if(auto r = builder(ss, I)) - { - writePage(ss.str(), symbolIdx); - } - else - { - r.error().Throw(); - } + // Output to an independent string first (async), then write to + // the shared stream (sync) + std::stringstream ss; + if(auto r = builder(ss, I)) + { + writePage(ss.str(), symbolIdx); + } + else + { + r.error().Throw(); + } }); } Corpus::TraverseOptions opts = {.skipInherited = std::same_as}; diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp index 0f4955bfe8..2ab9511ac5 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -129,9 +129,7 @@ inheritBaseMembers( // are the same auto const& otherFunc = static_cast(otherInfo); auto const& func = static_cast(info); - return - std::tie(func.Name, func.Params, func.Template) == - std::tie(otherFunc.Name, otherFunc.Params, func.Template); + return overrides(func, otherFunc); } // For other kinds of members, it's a shadow if the names // are the same diff --git a/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp b/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp index 5186e9a1e5..89fed02d3f 100644 --- a/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/NamespacesFinalizer.hpp @@ -16,13 +16,11 @@ namespace clang::mrdocs { -/** Finalizes a set of Info. +/** Finalizes the namespaces in corpus. - This removes any references to SymbolIDs - which do not exist. - - References which should always be valid - are not checked. + Namespaces might be removed or have + their extraction mode updated depending + on its members. */ class NamespacesFinalizer { diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp index 71a11493b9..e98bb24087 100644 --- a/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp @@ -28,54 +28,139 @@ foldRecordMembers(std::vector const& ids) void OverloadsFinalizer:: -foldNamespaceMembers(std::vector const& ids) +foldNamespaceMembers(std::vector const& namespaceIds) { - for (SymbolID const& id: ids) + for (SymbolID const& namespaceId: namespaceIds) { - Info* infoPtr = corpus_.find(id); - MRDOCS_CHECK_OR_CONTINUE(infoPtr); - auto* ns = dynamic_cast(infoPtr); + Info* namespaceInfoPtr = corpus_.find(namespaceId); + MRDOCS_CHECK_OR_CONTINUE(namespaceInfoPtr); + auto* ns = dynamic_cast(namespaceInfoPtr); MRDOCS_CHECK_OR_CONTINUE(ns); operator()(*ns); } } +namespace { +SymbolID +findBaseClassPermutation( + SymbolID const& contextId, + CorpusImpl& corpus, + ArrayRef sameNameFunctionIds) +{ + Info* info = corpus.find(contextId); + MRDOCS_CHECK_OR(info, SymbolID::invalid); + if (auto* record = dynamic_cast(info)) + { + bool overloadsFromBase = false; + for (auto const& base: record->Bases) + { + auto const baseInfo = corpus.find(base.Type->namedSymbol()); + MRDOCS_CHECK_OR_CONTINUE(baseInfo); + auto const baseRecord = dynamic_cast(baseInfo); + MRDOCS_CHECK_OR_CONTINUE(baseRecord); + RecordTranche* tranchesPtrs[] = { + &baseRecord->Interface.Public, + &baseRecord->Interface.Protected, + &baseRecord->Interface.Private, + }; + for (RecordTranche* tranchePtr: tranchesPtrs) + { + std::vector* trancheFunctionPtrs[] = { + &tranchePtr->Functions, + &tranchePtr->StaticFunctions + }; + for (std::vector* trancheFunctionsPtr: + trancheFunctionPtrs) + { + for (SymbolID const& baseID: *trancheFunctionsPtr) + { + Info* baseFuncMember = corpus.find(baseID); + MRDOCS_CHECK_OR_CONTINUE(baseFuncMember); + MRDOCS_CHECK_OR_CONTINUE(baseFuncMember->isOverloads()); + auto* overloads = dynamic_cast(baseFuncMember); + MRDOCS_CHECK_OR_CONTINUE(overloads); + // Does this overload set have the same functions + MRDOCS_CHECK_OR_CONTINUE( + std::ranges::is_permutation( + overloads->Members, + sameNameFunctionIds)); + return overloads->id; + } + } + } + } + } + return SymbolID::invalid; +} +} + void OverloadsFinalizer:: -foldOverloads(SymbolID const& parent, std::vector& ids) +foldOverloads(SymbolID const& contextId, std::vector& functionIds) { - for (auto it = ids.begin(); it != ids.end(); ++it) + for (auto functionIdIt = functionIds.begin(); + functionIdIt != functionIds.end(); + ++functionIdIt) { // Get the FunctionInfo for the current id - auto infoPtr = corpus_.find(*it); - MRDOCS_CHECK_OR_CONTINUE(infoPtr); - auto* function = dynamic_cast(infoPtr); + auto recordInfoPtr = corpus_.find(*functionIdIt); + MRDOCS_CHECK_OR_CONTINUE(recordInfoPtr); + auto* function = dynamic_cast(recordInfoPtr); MRDOCS_CHECK_OR_CONTINUE(function); // Check if the FunctionInfo is unique - auto sameNameIt = - std::find_if( - it + 1, - ids.end(), - [&](SymbolID const& otherID) - { - auto const otherInfoPtr = corpus_.find(otherID); - MRDOCS_CHECK_OR(otherInfoPtr, false); - Info& otherInfo = *otherInfoPtr; - return function->Name == otherInfo.Name; - }); - if (sameNameIt == ids.end()) + std::ranges::subrange otherFunctionIds( + std::next(functionIdIt), + functionIds.end()); + auto isSameNameFunction = [&](SymbolID const& otherID) { + auto const otherFunctionPtr = corpus_.find(otherID); + MRDOCS_CHECK_OR(otherFunctionPtr, false); + Info const& otherInfo = *otherFunctionPtr; + return function->Name == otherInfo.Name; + }; + auto sameNameIt = std::ranges:: + find_if(otherFunctionIds, isSameNameFunction); + bool const isUniqueFunction = sameNameIt == otherFunctionIds.end(); + MRDOCS_CHECK_OR_CONTINUE(!isUniqueFunction); + + // Create a list of FunctionInfo overloads + auto sameNameFunctionIdsView = + std::ranges::subrange(functionIdIt, functionIds.end()) | + std::views::filter(isSameNameFunction); + SmallVector sameNameFunctionIds( + sameNameFunctionIdsView.begin(), + sameNameFunctionIdsView.end()); + + // Check if any of the base classes has an overload set + // with the exact same function ids. If that's the case, + // the function will create a reference. + SymbolID equivalentOverloadsID = findBaseClassPermutation( + contextId, corpus_, sameNameFunctionIds); + if (equivalentOverloadsID) { + MRDOCS_ASSERT(corpus_.find(equivalentOverloadsID)); + // This base overload set becomes the + // representation in the record + *functionIdIt = equivalentOverloadsID; + auto const offset = functionIdIt - functionIds.begin(); + // Erase the other function ids with + // the same name + for (SymbolID sameNameId: sameNameFunctionIds) + { + std::erase(functionIds, sameNameId); + } + functionIdIt = functionIds.begin() + offset; continue; } - // FunctionInfo is not unique, so merge it with the other FunctionInfos - // into an OverloadsInfo - OverloadsInfo O(parent, function->Name); + // FunctionInfo is not unique and there's no equivalent + // overload set in base classes, so we merge it with the + // other FunctionInfos into a new OverloadsInfo + OverloadsInfo O(contextId, function->Name); addMember(O, *function); - *it = O.id; - auto const itOffset = it - ids.begin(); - for (auto otherIt = it + 1; otherIt != ids.end(); ++otherIt) + *functionIdIt = O.id; + auto const itOffset = functionIdIt - functionIds.begin(); + for (auto otherIt = functionIdIt + 1; otherIt != functionIds.end(); ++otherIt) { Info* otherInfoPtr = corpus_.find(*otherIt); MRDOCS_CHECK_OR_CONTINUE(otherInfoPtr); @@ -84,10 +169,10 @@ foldOverloads(SymbolID const& parent, std::vector& ids) if (function->Name == otherFunction->Name) { addMember(O, *otherFunction); - otherIt = std::prev(ids.erase(otherIt)); + otherIt = std::prev(functionIds.erase(otherIt)); } } - it = ids.begin() + itOffset; + functionIdIt = functionIds.begin() + itOffset; corpus_.info_.emplace(std::make_unique(std::move(O))); } } @@ -105,6 +190,22 @@ void OverloadsFinalizer:: operator()(RecordInfo& I) { + MRDOCS_CHECK_OR(!finalized_.contains(I.id)); + for (auto& b: I.Bases) + { + auto& BT = b.Type; + MRDOCS_CHECK_OR(BT); + MRDOCS_CHECK_OR(BT->isNamed()); + auto& NT = dynamic_cast(*BT); + MRDOCS_CHECK_OR(NT.Name); + auto& NI = dynamic_cast(*NT.Name); + MRDOCS_CHECK_OR(NI.id); + Info* baseInfo = corpus_.find(NI.id); + MRDOCS_CHECK_OR(baseInfo); + auto* baseRecord = dynamic_cast(baseInfo); + MRDOCS_CHECK_OR(baseRecord); + operator()(*baseRecord); + } foldOverloads(I.id, I.Interface.Public.Functions); foldOverloads(I.id, I.Interface.Protected.Functions); foldOverloads(I.id, I.Interface.Private.Functions); @@ -114,6 +215,7 @@ operator()(RecordInfo& I) foldRecordMembers(I.Interface.Public.Records); foldRecordMembers(I.Interface.Protected.Records); foldRecordMembers(I.Interface.Private.Records); + finalized_.emplace(I.id); } } // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp index ed42df6ec7..fc5c70aff9 100644 --- a/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp @@ -27,15 +27,18 @@ namespace clang::mrdocs { class OverloadsFinalizer { CorpusImpl& corpus_; + std::set finalized_; void foldRecordMembers(std::vector const& ids); void - foldNamespaceMembers(std::vector const& ids); + foldNamespaceMembers(std::vector const& namespaceIds); void - foldOverloads(SymbolID const& parent, std::vector& ids); + foldOverloads( + SymbolID const& contextId, + std::vector& functionIds); public: OverloadsFinalizer(CorpusImpl& corpus) diff --git a/src/lib/Metadata/Info/Function.cpp b/src/lib/Metadata/Info/Function.cpp index df247a7c11..ecd8798329 100644 --- a/src/lib/Metadata/Info/Function.cpp +++ b/src/lib/Metadata/Info/Function.cpp @@ -356,6 +356,22 @@ merge(FunctionInfo& I, FunctionInfo&& Other) } } +MRDOCS_DECL +bool +overrides(FunctionInfo const& base, FunctionInfo const& derived) +{ + auto toOverrideTuple = [](FunctionInfo const& f) { + return std::forward_as_tuple( + f.Name, + f.Params, + f.Template, + f.IsVariadic, + f.IsConst, + f.RefQualifier + ); + }; + return toOverrideTuple(base) == toOverrideTuple(derived); +} } // clang::mrdocs diff --git a/test-files/golden-tests/config/inherit-base-members/base-overload-set.adoc b/test-files/golden-tests/config/inherit-base-members/base-overload-set.adoc new file mode 100644 index 0000000000..bb44a89f65 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/base-overload-set.adoc @@ -0,0 +1,168 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols=1] +|=== +| Name +| <> +| <> +| <> +|=== + +[#Base] +== Base + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class Base + : public <>; +---- + +=== Member Functions + +[cols=2] +|=== +| Name +| Description +| <> +| +|=== + +=== Derived Classes + +[,cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#Base-foo-04] +== <>::foo + +=== Synopses + +Declared in `<base‐overload‐set.cpp>` + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +<>(); +---- + +[.small]#<># + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +<>() const; +---- + +[.small]#<># + +[#Base-foo-0a] +== <>::foo + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +foo(); +---- + +[#Base-foo-08] +== <>::foo + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +foo() const; +---- + +[#C] +== C + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class C + : public <>; +---- + +=== Member Functions + +[cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#ConstBase] +== ConstBase + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class ConstBase; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +=== Derived Classes + +[,cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#ConstBase-foo] +== <>::foo + +=== Synopsis + +Declared in `<base‐overload‐set.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +foo() const; +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/inherit-base-members/base-overload-set.cpp b/test-files/golden-tests/config/inherit-base-members/base-overload-set.cpp new file mode 100644 index 0000000000..6b24ec67e3 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/base-overload-set.cpp @@ -0,0 +1,13 @@ +class ConstBase { +public: + int& foo() const; +}; + +class Base + : public ConstBase { +public: + int& foo(); + int& foo() const; +}; + +class C : public Base {}; \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/base-overload-set.html b/test-files/golden-tests/config/inherit-base-members/base-overload-set.html new file mode 100644 index 0000000000..d45092d912 --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/base-overload-set.html @@ -0,0 +1,233 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + + + +
Name
Base
C
ConstBase
+ +
+
+
+

Base

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+class Base
+    : public ConstBase;
+
+
+
+

Member Functions

+ + + + + + + + + + + +
NameDescription
foo
+ + + +
+

Derived Classes

+ + + + + + + + + + +
NameDescription
C +
+
+
+
+
+

Base::foo

+
+
+

Synopses

+
+Declared in <base-overload-set.cpp>
+ +
+
+int&
+foo();
+
+
» more... + + +
+
+int&
+foo() const;
+
+
» more... + + +
+
+
+
+

Base::foo

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+int&
+foo();
+
+
+
+
+
+
+

Base::foo

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+int&
+foo() const;
+
+
+
+
+
+
+

C

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+class C
+    : public Base;
+
+
+
+

Member Functions

+ + + + + + + + + + + +
NameDescription
foo
+ + + +
+
+
+

ConstBase

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+class ConstBase;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
foo
+ + + +
+

Derived Classes

+ + + + + + + + + + +
NameDescription
Base +
+
+
+
+
+

ConstBase::foo

+
+
+

Synopsis

+
+Declared in <base-overload-set.cpp>
+
+
+int&
+foo() const;
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/inherit-base-members/base-overload-set.xml b/test-files/golden-tests/config/inherit-base-members/base-overload-set.xml new file mode 100644 index 0000000000..050fe0279f --- /dev/null +++ b/test-files/golden-tests/config/inherit-base-members/base-overload-set.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/config/overloads/const-mutable.adoc b/test-files/golden-tests/config/overloads/const-mutable.adoc new file mode 100644 index 0000000000..ad248a2f1e --- /dev/null +++ b/test-files/golden-tests/config/overloads/const-mutable.adoc @@ -0,0 +1,89 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols=1] +|=== +| Name +| <> +|=== + +[#C] +== C + +=== Synopsis + +Declared in `<const‐mutable.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +class C; +---- + +=== Member Functions + +[cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#C-foo-01] +== <>::foo + +=== Synopses + +Declared in `<const‐mutable.cpp>` + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +<>(); +---- + +[.small]#<># + + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +<>() const; +---- + +[.small]#<># + +[#C-foo-0b] +== <>::foo + +=== Synopsis + +Declared in `<const‐mutable.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +foo(); +---- + +[#C-foo-07] +== <>::foo + +=== Synopsis + +Declared in `<const‐mutable.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +foo() const; +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/overloads/const-mutable.cpp b/test-files/golden-tests/config/overloads/const-mutable.cpp new file mode 100644 index 0000000000..5abe33e90c --- /dev/null +++ b/test-files/golden-tests/config/overloads/const-mutable.cpp @@ -0,0 +1,5 @@ +class C { +public: + int& foo(); + int& foo() const; +}; diff --git a/test-files/golden-tests/config/overloads/const-mutable.html b/test-files/golden-tests/config/overloads/const-mutable.html new file mode 100644 index 0000000000..08aa8ab4ee --- /dev/null +++ b/test-files/golden-tests/config/overloads/const-mutable.html @@ -0,0 +1,122 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + +
Name
C
+ +
+
+
+

C

+
+
+

Synopsis

+
+Declared in <const-mutable.cpp>
+
+
+class C;
+
+
+
+

Member Functions

+ + + + + + + + + + + +
NameDescription
foo
+ + + +
+
+
+

C::foo

+
+
+

Synopses

+
+Declared in <const-mutable.cpp>
+ +
+
+int&
+foo();
+
+
» more... + + +
+
+int&
+foo() const;
+
+
» more... + + +
+
+
+
+

C::foo

+
+
+

Synopsis

+
+Declared in <const-mutable.cpp>
+
+
+int&
+foo();
+
+
+
+
+
+
+

C::foo

+
+
+

Synopsis

+
+Declared in <const-mutable.cpp>
+
+
+int&
+foo() const;
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/overloads/const-mutable.xml b/test-files/golden-tests/config/overloads/const-mutable.xml new file mode 100644 index 0000000000..1d522848c0 --- /dev/null +++ b/test-files/golden-tests/config/overloads/const-mutable.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + +