Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/mrdocs/Metadata/Symbol/Function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ struct FunctionSymbol final

Populated by `SpecializationFinalizer` with the IDs of
function-template specializations referring to this
function as their primary. Sorted by referent name
then ID.
function as their primary. Ordered like every other list
of symbols, which for these comes down to the arguments
they were specialized with, since they all carry this
function's name.
*/
std::vector<SymbolID> Specializations;

Expand Down
3 changes: 2 additions & 1 deletion include/mrdocs/Metadata/Symbol/Guide.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ struct GuideSymbol final
: SymbolCommonBase(ID)
{}

/** Compare guides by params/deduced/template/explicit.
/** Compare guides by what they deduce, then by their parameters
and template head.
*/
std::strong_ordering
operator<=>(GuideSymbol const& other) const;
Expand Down
10 changes: 7 additions & 3 deletions include/mrdocs/Metadata/Symbol/Record.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,19 @@ struct RecordSymbol final

Populated by `SpecializationFinalizer` with the IDs of
class-template specializations referring to this record
as their primary. Sorted by referent name then ID.
as their primary. Ordered like every other list of symbols,
which for these comes down to the arguments they were
specialized with, since they all carry this record's name.
*/
std::vector<SymbolID> Specializations;

/** Deduction guides associated with this class template.

Populated by `SpecializationFinalizer` with the IDs of
deduction guides that deduce this record. Sorted by
referent name then ID.
deduction guides that deduce this record. Ordered like every
other list of symbols, which for these comes down to what
each one deduces and then the types it takes, since they all
carry this record's name.
*/
std::vector<SymbolID> DeductionGuides;

Expand Down
251 changes: 1 addition & 250 deletions src/mrdocs/Metadata/Finalizers/SortMembersFinalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,261 +9,12 @@
//

#include "SortMembersFinalizer.hpp"
#include "SymbolIDCompare.hpp"
#include <algorithm>
#include <ranges>

namespace mrdocs {

namespace {
// Comparison function by symbol IDs
struct SymbolIDCompareFn
{
Corpus const& corpus_;
Config const& config_;

template <class InfoTy>
static
Optional<FunctionClass>
findFunctionClass(InfoTy const& I)
{
if constexpr (std::same_as<InfoTy, Symbol>)
{
return visit(I, []<class U>(U const& u)
-> Optional<FunctionClass>
{
return findFunctionClass<U>(u);
});
}
else if constexpr (
std::same_as<FunctionSymbol, InfoTy> ||
std::same_as<OverloadsSymbol, InfoTy>)
{
return I.FuncClass;
}
return std::nullopt;
}

template <class InfoTy>
static
Optional<OperatorKind>
findOperatorKind(InfoTy const& I)
{
if constexpr (std::same_as<InfoTy, Symbol>)
{
return visit(I, []<class U>(U const& u)
-> Optional<OperatorKind>
{
return findOperatorKind<U>(u);
});
}
else if constexpr (
std::same_as<FunctionSymbol, InfoTy> ||
std::same_as<OverloadsSymbol, InfoTy>)
{
return I.OverloadedOperator;
}
return std::nullopt;
}

bool
operator()(SymbolID const& lhsId, SymbolID const& rhsId) const
{
// Get Info from SymbolID
Symbol const* lhsPtr = corpus_.find(lhsId);
MRDOCS_CHECK_OR(lhsPtr, false);
Symbol const* rhsPtr = corpus_.find(rhsId);
MRDOCS_CHECK_OR(rhsPtr, true);
Symbol const& lhs = *lhsPtr;
Symbol const& rhs = *rhsPtr;

// Constructors come first
Optional<FunctionClass> const lhsClass = findFunctionClass(lhs);
Optional<FunctionClass> const rhsClass = findFunctionClass(rhs);
if (config_.sortMembersCtors1St)
{
bool const lhsIsCtor = lhsClass && *lhsClass == FunctionClass::Constructor;
bool const rhsIsCtor = rhsClass && *rhsClass == FunctionClass::Constructor;
if (lhsIsCtor != rhsIsCtor)
{
return lhsIsCtor;
}
}

// Destructors come next
if (config_.sortMembersDtors1St)
{
bool const lhsIsDtor = lhsClass && *lhsClass == FunctionClass::Destructor;
bool const rhsIsDtor = rhsClass && *rhsClass == FunctionClass::Destructor;
if (lhsIsDtor != rhsIsDtor)
{
return lhsIsDtor;
}
}

// Assignment operators come next
Optional<OperatorKind> const lhsOp = findOperatorKind(lhs);
Optional<OperatorKind> const rhsOp = findOperatorKind(rhs);
if (config_.sortMembersAssignment1St)
{
bool const lhsIsAssign = lhsOp && *lhsOp == OperatorKind::Equal;
bool const rhsIsAssign = rhsOp && *rhsOp == OperatorKind::Equal;
if (lhsIsAssign != rhsIsAssign)
{
return lhsIsAssign;
}
}

// Relational operators come last
if (config_.sortMembersRelationalLast)
{
bool const lhsIsRel = lhsOp && (
*lhsOp == OperatorKind::Exclaim ||
*lhsOp == OperatorKind::EqualEqual ||
*lhsOp == OperatorKind::ExclaimEqual ||
*lhsOp == OperatorKind::Less ||
*lhsOp == OperatorKind::Greater ||
*lhsOp == OperatorKind::LessEqual ||
*lhsOp == OperatorKind::GreaterEqual ||
*lhsOp == OperatorKind::Spaceship ||
*lhsOp == OperatorKind::LessLess);
bool const rhsIsRel = rhsOp && (
*rhsOp == OperatorKind::Exclaim ||
*rhsOp == OperatorKind::EqualEqual ||
*rhsOp == OperatorKind::ExclaimEqual ||
*rhsOp == OperatorKind::Less ||
*rhsOp == OperatorKind::Greater ||
*rhsOp == OperatorKind::LessEqual ||
*rhsOp == OperatorKind::GreaterEqual ||
*rhsOp == OperatorKind::Spaceship ||
*rhsOp == OperatorKind::LessLess);
if (lhsIsRel != rhsIsRel)
{
return !lhsIsRel;
}
if (lhsIsRel && rhsIsRel)
{
return std::is_lt(*lhsOp <=> *rhsOp);
}
}

// Conversion operators come last
if (config_.sortMembersConversionLast)
{
bool const lhsIsConvertion = lhsClass && *lhsClass == FunctionClass::Conversion;
bool const rhsIsConvertion = rhsClass && *rhsClass == FunctionClass::Conversion;
if (lhsIsConvertion != rhsIsConvertion)
{
return !lhsIsConvertion;
}
}

// If both are constructors/assignment with 1 parameter, the copy/move
// constructors come first
if ((lhsClass && *lhsClass == FunctionClass::Constructor &&
rhsClass && *rhsClass == FunctionClass::Constructor) ||
(lhsOp && *lhsOp == OperatorKind::Equal &&
rhsOp && *rhsOp == OperatorKind::Equal))
{
FunctionSymbol const& lhsF = lhs.asFunction();
FunctionSymbol const& rhsF = rhs.asFunction();
if (lhsF.Params.size() == 1 && rhsF.Params.size() == 1)
{
auto isCopyOrMoveConstOrAssign = [](FunctionSymbol const& I) {
if (I.Params.size() == 1)
{
auto const& param = I.Params[0];
Polymorphic<Type> const& paramType = param.Type;
MRDOCS_ASSERT(!paramType.valueless_after_move());
MRDOCS_CHECK_OR(
paramType->isLValueReference() ||
paramType->isRValueReference(), false);
Polymorphic<Type> const &paramRefPointeeOpt =
paramType->isLValueReference()
? paramType->asLValueReference().PointeeType
: paramType->asRValueReference().PointeeType;
MRDOCS_CHECK_OR(paramRefPointeeOpt, false);
auto const& paramRefPointee = *paramRefPointeeOpt;
if (!paramRefPointee.isNamed())
{
return false;
}
return paramRefPointee.namedSymbol() == I.Parent;
}
return false;
};

bool const lhsIsCopyOrMove = isCopyOrMoveConstOrAssign(lhsF);
bool const rhsIsCopyOrMove = isCopyOrMoveConstOrAssign(rhsF);
if (auto const cmp = lhsIsCopyOrMove <=> rhsIsCopyOrMove;
cmp != 0)
{
return !std::is_lt(cmp);
}
// Ensure move comes after copy
if (lhsIsCopyOrMove && rhsIsCopyOrMove)
{
MRDOCS_ASSERT(!lhsF.Params[0].Type.valueless_after_move());
MRDOCS_ASSERT(!rhsF.Params[0].Type.valueless_after_move());
bool const lhsIsMove = lhsF.Params[0].Type->isRValueReference();
bool const rhsIsMove = rhsF.Params[0].Type->isRValueReference();
if (lhsIsMove != rhsIsMove)
{
return !lhsIsMove;
}
}
}
}

// Special cases are handled, so use the configuration criteria
Symbol const* P = corpus_.find(lhs.Parent);
bool const isClassMember = P && P->isRecord();
auto const generalSortCriteria =
isClassMember
? config_.sortMembersBy
: config_.sortNamespaceMembersBy;
switch (generalSortCriteria)
{
case mrdocs::ConfigSchema::SortSymbolBy::Name:
if (auto const cmp = lhs.Name <=> rhs.Name; cmp != 0)
{
return std::is_lt(cmp);
}
break;
case mrdocs::ConfigSchema::SortSymbolBy::Location:
{
// By location: short path, line, column
auto const& lhsLoc = getPrimaryLocation(lhs);
auto const& rhsLoc = getPrimaryLocation(rhs);
if (auto const cmp = lhsLoc->ShortPath <=> rhsLoc->ShortPath;
cmp != 0)
{
return std::is_lt(cmp);
}
if (auto const cmp = lhsLoc->LineNumber <=> rhsLoc->LineNumber;
cmp != 0)
{
return std::is_lt(cmp);
}
if (auto const cmp = lhsLoc->ColumnNumber <=> rhsLoc->ColumnNumber;
cmp != 0)
{
return std::is_lt(cmp);
}
break;
}
default:
MRDOCS_UNREACHABLE();
}

// In case of a tie, we use the internal criteria for that symbol type
// to ensure a stable sort. For instance, in the case of functions,
// we sort by name, then number of parameters, then parameter types,
// and so on.
return std::is_lt(CompareDerived(lhs, rhs));
}
};
} // (anonymous)

void
SortMembersFinalizer::
sortMembers(std::vector<SymbolID>& ids)
Expand Down
22 changes: 5 additions & 17 deletions src/mrdocs/Metadata/Finalizers/SpecializationFinalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//

#include "SpecializationFinalizer.hpp"
#include "SymbolIDCompare.hpp"
#include <mrdocs/Support/Error/Assert.hpp>
#include <algorithm>
#include <ranges>
Expand Down Expand Up @@ -105,20 +106,7 @@ void
SpecializationFinalizer::
sortBackPointers()
{
auto byReferentName = [this](SymbolID const& lhs, SymbolID const& rhs)
{
Symbol const* lhsInfo = corpus_.find(lhs);
Symbol const* rhsInfo = corpus_.find(rhs);
if (!lhsInfo || !rhsInfo)
{
return lhs < rhs;
}
if (lhsInfo->Name != rhsInfo->Name)
{
return lhsInfo->Name < rhsInfo->Name;
}
return lhs < rhs;
};
SymbolIDCompareFn const pred{corpus_, config_};
for (Symbol const& I : corpus_)
{
if (I.isRecord())
Expand All @@ -127,8 +115,8 @@ sortBackPointers()
if (!R.Specializations.empty() || !R.DeductionGuides.empty())
{
RecordSymbol* mut = corpus_.find(I.id)->asRecordPtr();
std::ranges::sort(mut->Specializations, byReferentName);
std::ranges::sort(mut->DeductionGuides, byReferentName);
std::ranges::sort(mut->Specializations, pred);
std::ranges::sort(mut->DeductionGuides, pred);
}
}
else if (I.isFunction())
Expand All @@ -137,7 +125,7 @@ sortBackPointers()
if (!F.Specializations.empty())
{
FunctionSymbol* mut = corpus_.find(I.id)->asFunctionPtr();
std::ranges::sort(mut->Specializations, byReferentName);
std::ranges::sort(mut->Specializations, pred);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/mrdocs/Metadata/Finalizers/SpecializationFinalizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ namespace mrdocs {
`Specializations` list and sets the specialization's
`IsListedOnPrimary` flag. For each `Regular` deduction guide,
appends its ID to the deduced record's `DeductionGuides`
list. The populated lists are sorted by referent name then ID.
list. The populated lists are ordered with the same comparison
as every other list of symbols.

Orphan specializations - those whose primary is not extracted
in `Regular` mode - keep `IsListedOnPrimary` `false` so they
Expand Down Expand Up @@ -60,7 +61,8 @@ class SpecializationFinalizer
also regular) and each `Regular` deduction guide to its deduced
record's `DeductionGuides` list. Specializations whose primary
will be rendered also get their `IsListedOnPrimary` flag set.
Finally sorts every populated vector by referent name then ID.
Finally orders every populated vector with `SymbolIDCompareFn`,
the comparison used for every other list of symbols.
*/
void build();
};
Expand Down
Loading
Loading