diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 9efec0ae1b..7cd8a8efa9 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -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.", diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 661249539d..fe5106938e 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -116,6 +116,10 @@ struct SpecializationNameInfo final */ std::vector> TemplateArgs; + /** The SymbolID of the named symbol, if it exists. + */ + SymbolID specializationID = SymbolID::invalid; + constexpr SpecializationNameInfo() noexcept : NameInfo(NameKind::Specialization) diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index ada2b000a6..0a22e1300d 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -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(D); auto const* Templated = D; @@ -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(); + 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(*inner); + MRDOCS_CHECK_OR(NTI.Name); + MRDOCS_CHECK_OR(NTI.Name->isSpecialization()); + auto& SNI = dynamic_cast(*NTI.Name); + SNI.specializationID = SI->id; + }(); + } + // CXXBaseSpecifier::getEllipsisLoc indicates whether the // base was a pack expansion; a PackExpansionType is not built // for base-specifiers @@ -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 @@ -2728,7 +2767,7 @@ checkTypeFilters(Decl const* D, AccessSpecifier const access) auto const* RD = dyn_cast(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(D), false); return true; @@ -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 diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index 359b70d93a..48207615b7 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -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. diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 23d84ffd77..1779acf461 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -323,7 +323,7 @@ decayToPrimaryTemplate(Decl const* D) } bool -isAllImplicit(Decl const* D) +isAllImplicitSpecialization(Decl const* D) { if (!D) { @@ -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(D); + TSD && + !TSD->isExplicitSpecialization()) + { + return true; + } + if (auto const* TSD = dynamic_cast(D); + TSD && + !TSD->isExplicitSpecialization()) + { + return true; + } + auto const* P = getParent(D); + return isAnyImplicitSpecialization(P); } bool diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index d33c9bab89..e404fca1d3 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -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 diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index d87fc12867..ea02e4f047 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -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", diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp index feffff6e13..0f4955bfe8 100644 --- a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -83,7 +83,6 @@ namespace { bool shouldCopy(Config const& config, Info const& M) { - if (config->inheritBaseMembers == PublicSettings::BaseMemberInheritance::CopyDependencies) { return M.Extraction == ExtractionMode::Dependency; @@ -236,7 +235,14 @@ operator()(RecordInfo& I) MRDOCS_CHECK_OR_CONTINUE(baseNameType); auto const* baseName = get(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(baseName); + MRDOCS_CHECK_OR_CONTINUE(baseSpec); + baseID = baseSpec->specializationID; + } MRDOCS_CHECK_OR_CONTINUE(baseID); auto basePtr = corpus_.find(baseID); MRDOCS_CHECK_OR_CONTINUE(basePtr); diff --git a/test-files/golden-tests/config/extract-implicit-specializations/base.adoc b/test-files/golden-tests/config/extract-implicit-specializations/base.adoc new file mode 100644 index 0000000000..cbbc1194d8 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/base.adoc @@ -0,0 +1,81 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols=1] +|=== +| Name +| <> +| <> +|=== + +[#A] +== A + +=== Synopsis + +Declared in `<base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct A + : <>; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +[#B] +== B + +=== Synopsis + +Declared in `<base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct B; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +=== Derived Classes + +[,cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#B-value] +== <>::value + +=== Synopsis + +Declared in `<base.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int +value(); +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/extract-implicit-specializations/base.cpp b/test-files/golden-tests/config/extract-implicit-specializations/base.cpp new file mode 100644 index 0000000000..00fb046d45 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/base.cpp @@ -0,0 +1,5 @@ +struct B { + int value(); +}; + +struct A : public B {}; diff --git a/test-files/golden-tests/config/extract-implicit-specializations/base.html b/test-files/golden-tests/config/extract-implicit-specializations/base.html new file mode 100644 index 0000000000..207247cacd --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/base.html @@ -0,0 +1,125 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + + +
Name
A
B
+ +
+
+
+

A

+
+
+

Synopsis

+
+Declared in <base.cpp>
+
+
+struct A
+    : B;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+
+
+

B

+
+
+

Synopsis

+
+Declared in <base.cpp>
+
+
+struct B;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+

Derived Classes

+ + + + + + + + + + +
NameDescription
A +
+
+
+
+
+

B::value

+
+
+

Synopsis

+
+Declared in <base.cpp>
+
+
+int
+value();
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-implicit-specializations/base.xml b/test-files/golden-tests/config/extract-implicit-specializations/base.xml new file mode 100644 index 0000000000..4e4171a9b0 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/base.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/config/extract-implicit-specializations/base.yml b/test-files/golden-tests/config/extract-implicit-specializations/base.yml new file mode 100644 index 0000000000..d66e769231 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/base.yml @@ -0,0 +1 @@ +extract-implicit-specializations: true \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.adoc b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.adoc new file mode 100644 index 0000000000..d0210ca45b --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.adoc @@ -0,0 +1,95 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols=1] +|=== +| Name +| <> +| <> +|=== + +[#A] +== A + +=== Synopsis + +Declared in `<extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct A + : <><int>; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +[#A-value] +== <>::value + +=== Synopsis + +Declared in `<extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +int& +value(); +---- + +[#B-00] +== B + +=== Synopsis + +Declared in `<extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<typename T> +struct B; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +=== Derived Classes + +[,cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#B-00-value] +== <>::value + +=== Synopsis + +Declared in `<extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +value(); +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.cpp b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.cpp new file mode 100644 index 0000000000..da7f56885b --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.cpp @@ -0,0 +1,6 @@ +template +struct B { + T& value(); +}; + +struct A : public B {}; diff --git a/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.html b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.html new file mode 100644 index 0000000000..9c3d54b90e --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.html @@ -0,0 +1,142 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + + +
Name
A
B
+ +
+
+
+

A

+
+
+

Synopsis

+
+Declared in <extract-implicit-specializations.cpp>
+
+
+struct A
+    : B<int>;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+
+
+

A::value

+
+
+

Synopsis

+
+Declared in <extract-implicit-specializations.cpp>
+
+
+int&
+value();
+
+
+
+
+
+
+

B

+
+
+

Synopsis

+
+Declared in <extract-implicit-specializations.cpp>
+
+
+template<typename T>
+struct B;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+

Derived Classes

+ + + + + + + + + + +
NameDescription
A +
+
+
+
+
+

B::value

+
+
+

Synopsis

+
+Declared in <extract-implicit-specializations.cpp>
+
+
+T&
+value();
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.xml b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.xml new file mode 100644 index 0000000000..fb77b9641e --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.yml b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.yml new file mode 100644 index 0000000000..d66e769231 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/extract-implicit-specializations.yml @@ -0,0 +1 @@ +extract-implicit-specializations: true \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.adoc b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.adoc new file mode 100644 index 0000000000..2ff23cf2e9 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.adoc @@ -0,0 +1,82 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Types + +[cols=1] +|=== +| Name +| <> +| <> +|=== + +[#A] +== A + +=== Synopsis + +Declared in `<no‐extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct A + : <><int>; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +[#B] +== B + +=== Synopsis + +Declared in `<no‐extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<typename T> +struct B; +---- + +=== Member Functions + +[cols=1] +|=== +| Name +| <> +|=== + +=== Derived Classes + +[,cols=2] +|=== +| Name +| Description +| <> +| +|=== + +[#B-value] +== <>::value + +=== Synopsis + +Declared in `<no‐extract‐implicit‐specializations.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +T& +value(); +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.cpp b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.cpp new file mode 100644 index 0000000000..da7f56885b --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.cpp @@ -0,0 +1,6 @@ +template +struct B { + T& value(); +}; + +struct A : public B {}; diff --git a/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.html b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.html new file mode 100644 index 0000000000..48c618cc73 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.html @@ -0,0 +1,126 @@ + + +Reference + + +
+

Reference

+
+
+

Global namespace

+
+

Types

+ + + + + + + + + + + +
Name
A
B
+ +
+
+
+

A

+
+
+

Synopsis

+
+Declared in <no-extract-implicit-specializations.cpp>
+
+
+struct A
+    : B<int>;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+
+
+

B

+
+
+

Synopsis

+
+Declared in <no-extract-implicit-specializations.cpp>
+
+
+template<typename T>
+struct B;
+
+
+
+

Member Functions

+ + + + + + + + + + +
Name
value
+ + + +
+

Derived Classes

+ + + + + + + + + + +
NameDescription
A +
+
+
+
+
+

B::value

+
+
+

Synopsis

+
+Declared in <no-extract-implicit-specializations.cpp>
+
+
+T&
+value();
+
+
+
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.xml b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.xml new file mode 100644 index 0000000000..4cc0a02123 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.yml b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.yml new file mode 100644 index 0000000000..2b0d7ee743 --- /dev/null +++ b/test-files/golden-tests/config/extract-implicit-specializations/no-extract-implicit-specializations.yml @@ -0,0 +1 @@ +extract-implicit-specializations: false \ No newline at end of file diff --git a/test-files/golden-tests/symbols/record/class-template-specializations-1.yml b/test-files/golden-tests/symbols/record/class-template-specializations-1.yml new file mode 100644 index 0000000000..2b0d7ee743 --- /dev/null +++ b/test-files/golden-tests/symbols/record/class-template-specializations-1.yml @@ -0,0 +1 @@ +extract-implicit-specializations: false \ No newline at end of file diff --git a/test-files/golden-tests/symbols/record/template-specialization-inheritance.yml b/test-files/golden-tests/symbols/record/template-specialization-inheritance.yml new file mode 100644 index 0000000000..2b0d7ee743 --- /dev/null +++ b/test-files/golden-tests/symbols/record/template-specialization-inheritance.yml @@ -0,0 +1 @@ +extract-implicit-specializations: false \ No newline at end of file diff --git a/test-files/golden-tests/symbols/using/dependency-propagation.yml b/test-files/golden-tests/symbols/using/dependency-propagation.yml index 65da888ff8..c4f9526061 100644 --- a/test-files/golden-tests/symbols/using/dependency-propagation.yml +++ b/test-files/golden-tests/symbols/using/dependency-propagation.yml @@ -3,3 +3,4 @@ filters: exclude: - 'N' referenced-declarations: dependency +extract-implicit-specializations: false \ No newline at end of file