Skip to content

Commit b1d3f24

Browse files
committed
extract-implicit-specializations option
#feat
1 parent b29ba25 commit b1d3f24

26 files changed

+887
-8
lines changed

docs/mrdocs.schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@
121121
"title": "Extraction policy for empty namespaces",
122122
"type": "boolean"
123123
},
124+
"extract-implicit-specializations": {
125+
"default": true,
126+
"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.",
127+
"enum": [
128+
true,
129+
false
130+
],
131+
"title": "Implicit template specializations used as base classes are extracted as dependencies",
132+
"type": "boolean"
133+
},
124134
"extract-local-classes": {
125135
"default": true,
126136
"description": "Determine whether records only defined locally in source files should be extracted.",

include/mrdocs/Metadata/Name.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ struct SpecializationNameInfo final
116116
*/
117117
std::vector<Polymorphic<TArg>> TemplateArgs;
118118

119+
/** The SymbolID of the named symbol, if it exists.
120+
*/
121+
SymbolID specializationID = SymbolID::invalid;
122+
119123
constexpr
120124
SpecializationNameInfo() noexcept
121125
: NameInfo(NameKind::Specialization)

src/lib/AST/ASTVisitor.cpp

+41-2
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,9 @@ generateUSR(Decl const* D) const
430430
}
431431

432432
if (index::generateUSRForDecl(D, res))
433+
{
433434
return Unexpected(Error("Failed to generate USR"));
435+
}
434436

435437
auto const* Described = dyn_cast_if_present<TemplateDecl>(D);
436438
auto const* Templated = D;
@@ -668,6 +670,37 @@ populate(
668670
QualType const BT = B.getType();
669671
auto BaseType = toTypeInfo(BT, BaseClass);
670672

673+
// If we're going to copy the members from the specialization,
674+
// we need to instantiate and traverse the specialization
675+
// as a dependency.
676+
if (config_->extractImplicitSpecializations)
677+
{
678+
[&] {
679+
auto* TST = BT->getAs<TemplateSpecializationType>();
680+
MRDOCS_CHECK_OR(TST);
681+
MRDOCS_SYMBOL_TRACE(TST, context_);
682+
683+
auto const* CTSD = dyn_cast_or_null<
684+
ClassTemplateSpecializationDecl>(
685+
TST->getAsCXXRecordDecl());
686+
MRDOCS_CHECK_OR(CTSD);
687+
MRDOCS_SYMBOL_TRACE(CTSD, context_);
688+
689+
// Traverse the Decl as a dependency
690+
ScopeExitRestore s(mode_, TraversalMode::BaseClass);
691+
Info const* SI = findOrTraverse(CTSD);
692+
MRDOCS_CHECK_OR(SI);
693+
auto& inner = innermostType(BaseType);
694+
MRDOCS_CHECK_OR(inner);
695+
MRDOCS_CHECK_OR(inner->isNamed());
696+
auto& NTI = dynamic_cast<NamedTypeInfo&>(*inner);
697+
MRDOCS_CHECK_OR(NTI.Name);
698+
MRDOCS_CHECK_OR(NTI.Name->isSpecialization());
699+
auto& SNI = dynamic_cast<SpecializationNameInfo&>(*NTI.Name);
700+
SNI.specializationID = SI->id;
701+
}();
702+
}
703+
671704
// CXXBaseSpecifier::getEllipsisLoc indicates whether the
672705
// base was a pack expansion; a PackExpansionType is not built
673706
// for base-specifiers
@@ -2659,6 +2692,12 @@ checkFilters(
26592692
Decl const* D,
26602693
AccessSpecifier const access)
26612694
{
2695+
if (mode_ == BaseClass &&
2696+
isAnyImplicitSpecialization(D))
2697+
{
2698+
return ExtractionMode::Dependency;
2699+
}
2700+
26622701
// The translation unit is always extracted as the
26632702
// global namespace. It can't fail any of the filters
26642703
// because its qualified name is represented by the
@@ -2728,7 +2767,7 @@ checkTypeFilters(Decl const* D, AccessSpecifier const access)
27282767
auto const* RD = dyn_cast<RecordDecl>(D);
27292768
MRDOCS_CHECK_OR(!RD || !RD->isAnonymousStructOrUnion(), false);
27302769

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

27342773
return true;
@@ -3327,7 +3366,7 @@ upsert(DeclType const* D)
33273366
{
33283367
return Unexpected(Error("Symbol should not be extracted"));
33293368
}
3330-
if (!isAllImplicit(D))
3369+
if (isAnyExplicitSpecialization(D))
33313370
{
33323371
// We should not extract explicit declarations in dependency mode.
33333372
// The calling code should handle this case instead of

src/lib/AST/ASTVisitor.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class ASTVisitor
199199

200200
/* All symbols are extracted
201201
202-
Even excluded symbols are traversed. If a sy bol
202+
Even excluded symbols are traversed. If a symbol
203203
doesn't pass the filters, it is extracted as a
204204
dependency.
205205

src/lib/AST/ClangHelpers.cpp

+25-2
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ decayToPrimaryTemplate(Decl const* D)
323323
}
324324

325325
bool
326-
isAllImplicit(Decl const* D)
326+
isAllImplicitSpecialization(Decl const* D)
327327
{
328328
if (!D)
329329
{
@@ -342,7 +342,30 @@ isAllImplicit(Decl const* D)
342342
return false;
343343
}
344344
auto const* P = getParent(D);
345-
return isAllImplicit(P);
345+
return isAllImplicitSpecialization(P);
346+
}
347+
348+
bool
349+
isAnyImplicitSpecialization(Decl const* D)
350+
{
351+
if (!D)
352+
{
353+
return false;
354+
}
355+
if (auto const* TSD = dynamic_cast<ClassTemplateSpecializationDecl const*>(D);
356+
TSD &&
357+
!TSD->isExplicitSpecialization())
358+
{
359+
return true;
360+
}
361+
if (auto const* TSD = dynamic_cast<VarTemplateSpecializationDecl const*>(D);
362+
TSD &&
363+
!TSD->isExplicitSpecialization())
364+
{
365+
return true;
366+
}
367+
auto const* P = getParent(D);
368+
return isAnyImplicitSpecialization(P);
346369
}
347370

348371
bool

src/lib/AST/ClangHelpers.hpp

+22-1
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,28 @@ decayToPrimaryTemplate(Decl const* D);
887887
// template specializations.
888888
MRDOCS_DECL
889889
bool
890-
isAllImplicit(Decl const* D);
890+
isAllImplicitSpecialization(Decl const* D);
891+
892+
// Check if any component of D is an implicit specialization
893+
MRDOCS_DECL
894+
bool
895+
isAnyImplicitSpecialization(Decl const* D);
896+
897+
// Check if at least one component of D is explicit
898+
inline
899+
bool
900+
isAnyExplicitSpecialization(Decl const* D)
901+
{
902+
return !isAllImplicitSpecialization(D);
903+
}
904+
905+
// Check if all components are explicit
906+
inline
907+
bool
908+
isAllExplicitSpecialization(Decl const* D)
909+
{
910+
return !isAnyImplicitSpecialization(D);
911+
}
891912

892913
MRDOCS_DECL
893914
bool

src/lib/Lib/ConfigOptions.json

+7
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@
254254
],
255255
"default": "copy-dependencies"
256256
},
257+
{
258+
"name": "extract-implicit-specializations",
259+
"brief": "Implicit template specializations used as base classes are extracted as dependencies",
260+
"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.",
261+
"type": "bool",
262+
"default": true
263+
},
257264
{
258265
"name": "sort-members",
259266
"brief": "Sort the members of a record or namespace",

src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ namespace {
8383
bool
8484
shouldCopy(Config const& config, Info const& M)
8585
{
86-
8786
if (config->inheritBaseMembers == PublicSettings::BaseMemberInheritance::CopyDependencies)
8887
{
8988
return M.Extraction == ExtractionMode::Dependency;
@@ -236,7 +235,14 @@ operator()(RecordInfo& I)
236235
MRDOCS_CHECK_OR_CONTINUE(baseNameType);
237236
auto const* baseName = get<NameInfo const*>(baseNameType->Name);
238237
MRDOCS_CHECK_OR_CONTINUE(baseName);
239-
SymbolID const baseID = baseName->id;
238+
SymbolID baseID = baseName->id;
239+
if (corpus_.config->extractImplicitSpecializations &&
240+
baseName->isSpecialization())
241+
{
242+
auto const* baseSpec = dynamic_cast<SpecializationNameInfo const*>(baseName);
243+
MRDOCS_CHECK_OR_CONTINUE(baseSpec);
244+
baseID = baseSpec->specializationID;
245+
}
240246
MRDOCS_CHECK_OR_CONTINUE(baseID);
241247
auto basePtr = corpus_.find(baseID);
242248
MRDOCS_CHECK_OR_CONTINUE(basePtr);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
= Reference
2+
:mrdocs:
3+
4+
[#index]
5+
== Global namespace
6+
7+
=== Types
8+
9+
[cols=1]
10+
|===
11+
| Name
12+
| <<A,`A`>>
13+
| <<B,`B`>>
14+
|===
15+
16+
[#A]
17+
== A
18+
19+
=== Synopsis
20+
21+
Declared in `&lt;base&period;cpp&gt;`
22+
23+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
24+
----
25+
struct A
26+
: <<B,B>>;
27+
----
28+
29+
=== Member Functions
30+
31+
[cols=1]
32+
|===
33+
| Name
34+
| <<B-value,`value`>>
35+
|===
36+
37+
[#B]
38+
== B
39+
40+
=== Synopsis
41+
42+
Declared in `&lt;base&period;cpp&gt;`
43+
44+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
45+
----
46+
struct B;
47+
----
48+
49+
=== Member Functions
50+
51+
[cols=1]
52+
|===
53+
| Name
54+
| <<B-value,`value`>>
55+
|===
56+
57+
=== Derived Classes
58+
59+
[,cols=2]
60+
|===
61+
| Name
62+
| Description
63+
| <<A,`A`>>
64+
|
65+
|===
66+
67+
[#B-value]
68+
== <<B,B>>::value
69+
70+
=== Synopsis
71+
72+
Declared in `&lt;base&period;cpp&gt;`
73+
74+
[source,cpp,subs="verbatim,replacements,macros,-callouts"]
75+
----
76+
int
77+
value();
78+
----
79+
80+
81+
[.small]#Created with https://www.mrdocs.com[MrDocs]#
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct B {
2+
int value();
3+
};
4+
5+
struct A : public B {};

0 commit comments

Comments
 (0)