Skip to content
Merged
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
10 changes: 10 additions & 0 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@
"title": "Extraction policy for empty namespaces",
"type": "boolean"
},
"extract-implicit-specializations": {
"default": true,
"description": "When set to `true`, MrDocs extracts implicit template specializations used as base classes as dependencies. This allows MrDocs to extract metadata that can later be used to determine the members of the derived class, as specified by the `inherit-base-members` option.",
"enum": [
true,
false
],
"title": "Implicit template specializations used as base classes are extracted as dependencies",
"type": "boolean"
},
"extract-local-classes": {
"default": true,
"description": "Determine whether records only defined locally in source files should be extracted.",
Expand Down
4 changes: 4 additions & 0 deletions include/mrdocs/Metadata/Name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ struct SpecializationNameInfo final
*/
std::vector<Polymorphic<TArg>> TemplateArgs;

/** The SymbolID of the named symbol, if it exists.
*/
SymbolID specializationID = SymbolID::invalid;

constexpr
SpecializationNameInfo() noexcept
: NameInfo(NameKind::Specialization)
Expand Down
43 changes: 41 additions & 2 deletions src/lib/AST/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,9 @@ generateUSR(Decl const* D) const
}

if (index::generateUSRForDecl(D, res))
{
return Unexpected(Error("Failed to generate USR"));
}

auto const* Described = dyn_cast_if_present<TemplateDecl>(D);
auto const* Templated = D;
Expand Down Expand Up @@ -668,6 +670,37 @@ populate(
QualType const BT = B.getType();
auto BaseType = toTypeInfo(BT, BaseClass);

// If we're going to copy the members from the specialization,
// we need to instantiate and traverse the specialization
// as a dependency.
if (config_->extractImplicitSpecializations)
{
[&] {
auto* TST = BT->getAs<TemplateSpecializationType>();
MRDOCS_CHECK_OR(TST);
MRDOCS_SYMBOL_TRACE(TST, context_);

auto const* CTSD = dyn_cast_or_null<
ClassTemplateSpecializationDecl>(
TST->getAsCXXRecordDecl());
MRDOCS_CHECK_OR(CTSD);
MRDOCS_SYMBOL_TRACE(CTSD, context_);

// Traverse the Decl as a dependency
ScopeExitRestore s(mode_, TraversalMode::BaseClass);
Info const* SI = findOrTraverse(CTSD);
MRDOCS_CHECK_OR(SI);
auto& inner = innermostType(BaseType);
MRDOCS_CHECK_OR(inner);
MRDOCS_CHECK_OR(inner->isNamed());
auto& NTI = dynamic_cast<NamedTypeInfo&>(*inner);
MRDOCS_CHECK_OR(NTI.Name);
MRDOCS_CHECK_OR(NTI.Name->isSpecialization());
auto& SNI = dynamic_cast<SpecializationNameInfo&>(*NTI.Name);
SNI.specializationID = SI->id;
}();
}

// CXXBaseSpecifier::getEllipsisLoc indicates whether the
// base was a pack expansion; a PackExpansionType is not built
// for base-specifiers
Expand Down Expand Up @@ -2659,6 +2692,12 @@ checkFilters(
Decl const* D,
AccessSpecifier const access)
{
if (mode_ == BaseClass &&
isAnyImplicitSpecialization(D))
{
return ExtractionMode::Dependency;
}

// The translation unit is always extracted as the
// global namespace. It can't fail any of the filters
// because its qualified name is represented by the
Expand Down Expand Up @@ -2728,7 +2767,7 @@ checkTypeFilters(Decl const* D, AccessSpecifier const access)
auto const* RD = dyn_cast<RecordDecl>(D);
MRDOCS_CHECK_OR(!RD || !RD->isAnonymousStructOrUnion(), false);

// Don't extract implicitly generated declarations
// Don't extract declarations implicitly generated by the compiler
MRDOCS_CHECK_OR(!D->isImplicit() || isa<IndirectFieldDecl>(D), false);

return true;
Expand Down Expand Up @@ -3327,7 +3366,7 @@ upsert(DeclType const* D)
{
return Unexpected(Error("Symbol should not be extracted"));
}
if (!isAllImplicit(D))
if (isAnyExplicitSpecialization(D))
{
// We should not extract explicit declarations in dependency mode.
// The calling code should handle this case instead of
Expand Down
2 changes: 1 addition & 1 deletion src/lib/AST/ASTVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class ASTVisitor

/* All symbols are extracted
Even excluded symbols are traversed. If a sy bol
Even excluded symbols are traversed. If a symbol
doesn't pass the filters, it is extracted as a
dependency.
Expand Down
27 changes: 25 additions & 2 deletions src/lib/AST/ClangHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ decayToPrimaryTemplate(Decl const* D)
}

bool
isAllImplicit(Decl const* D)
isAllImplicitSpecialization(Decl const* D)
{
if (!D)
{
Expand All @@ -342,7 +342,30 @@ isAllImplicit(Decl const* D)
return false;
}
auto const* P = getParent(D);
return isAllImplicit(P);
return isAllImplicitSpecialization(P);
}

bool
isAnyImplicitSpecialization(Decl const* D)
{
if (!D)
{
return false;
}
if (auto const* TSD = dynamic_cast<ClassTemplateSpecializationDecl const*>(D);
TSD &&
!TSD->isExplicitSpecialization())
{
return true;
}
if (auto const* TSD = dynamic_cast<VarTemplateSpecializationDecl const*>(D);
TSD &&
!TSD->isExplicitSpecialization())
{
return true;
}
auto const* P = getParent(D);
return isAnyImplicitSpecialization(P);
}

bool
Expand Down
23 changes: 22 additions & 1 deletion src/lib/AST/ClangHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,28 @@ decayToPrimaryTemplate(Decl const* D);
// template specializations.
MRDOCS_DECL
bool
isAllImplicit(Decl const* D);
isAllImplicitSpecialization(Decl const* D);

// Check if any component of D is an implicit specialization
MRDOCS_DECL
bool
isAnyImplicitSpecialization(Decl const* D);

// Check if at least one component of D is explicit
inline
bool
isAnyExplicitSpecialization(Decl const* D)
{
return !isAllImplicitSpecialization(D);
}

// Check if all components are explicit
inline
bool
isAllExplicitSpecialization(Decl const* D)
{
return !isAnyImplicitSpecialization(D);
}

MRDOCS_DECL
bool
Expand Down
7 changes: 7 additions & 0 deletions src/lib/Lib/ConfigOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,13 @@
],
"default": "copy-dependencies"
},
{
"name": "extract-implicit-specializations",
"brief": "Implicit template specializations used as base classes are extracted as dependencies",
"details": "When set to `true`, MrDocs extracts implicit template specializations used as base classes as dependencies. This allows MrDocs to extract metadata that can later be used to determine the members of the derived class, as specified by the `inherit-base-members` option.",
"type": "bool",
"default": true
},
{
"name": "sort-members",
"brief": "Sort the members of a record or namespace",
Expand Down
10 changes: 8 additions & 2 deletions src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ namespace {
bool
shouldCopy(Config const& config, Info const& M)
{

if (config->inheritBaseMembers == PublicSettings::BaseMemberInheritance::CopyDependencies)
{
return M.Extraction == ExtractionMode::Dependency;
Expand Down Expand Up @@ -236,7 +235,14 @@ operator()(RecordInfo& I)
MRDOCS_CHECK_OR_CONTINUE(baseNameType);
auto const* baseName = get<NameInfo const*>(baseNameType->Name);
MRDOCS_CHECK_OR_CONTINUE(baseName);
SymbolID const baseID = baseName->id;
SymbolID baseID = baseName->id;
if (corpus_.config->extractImplicitSpecializations &&
baseName->isSpecialization())
{
auto const* baseSpec = dynamic_cast<SpecializationNameInfo const*>(baseName);
MRDOCS_CHECK_OR_CONTINUE(baseSpec);
baseID = baseSpec->specializationID;
}
MRDOCS_CHECK_OR_CONTINUE(baseID);
auto basePtr = corpus_.find(baseID);
MRDOCS_CHECK_OR_CONTINUE(basePtr);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
= Reference
:mrdocs:

[#index]
== Global namespace

=== Types

[cols=1]
|===
| Name
| <<A,`A`>>
| <<B,`B`>>
|===

[#A]
== A

=== Synopsis

Declared in `&lt;base&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
struct A
: <<B,B>>;
----

=== Member Functions

[cols=1]
|===
| Name
| <<B-value,`value`>>
|===

[#B]
== B

=== Synopsis

Declared in `&lt;base&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
struct B;
----

=== Member Functions

[cols=1]
|===
| Name
| <<B-value,`value`>>
|===

=== Derived Classes

[,cols=2]
|===
| Name
| Description
| <<A,`A`>>
|
|===

[#B-value]
== <<B,B>>::value

=== Synopsis

Declared in `&lt;base&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
int
value();
----


[.small]#Created with https://www.mrdocs.com[MrDocs]#
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
struct B {
int value();
};

struct A : public B {};
Loading
Loading