diff --git a/src/lib/Gen/adoc/DocVisitor.cpp b/src/lib/Gen/adoc/DocVisitor.cpp index 12c1335553..0916eb302e 100644 --- a/src/lib/Gen/adoc/DocVisitor.cpp +++ b/src/lib/Gen/adoc/DocVisitor.cpp @@ -244,15 +244,24 @@ operator()(doc::Reference const& I) const { return (*this)(static_cast(I)); } - auto url = corpus_.getURL(corpus_->get(I.id)); - if (url.starts_with('/')) + dom::Object symbolObj = corpus_.construct(corpus_->get(I.id)); + if (symbolObj.exists("url")) { - url.erase(0, 1); + std::string url = symbolObj.get("url").getString().str(); + if (url.starts_with('/')) + { + url.erase(0, 1); + } + fmt::format_to( + std::back_inserter(dest_), + "xref:{}[{}]", + url, AdocEscape(I.string)); + return; } fmt::format_to( - std::back_inserter(dest_), - "xref:{}[{}]", - url, AdocEscape(I.string)); + std::back_inserter(dest_), + "`{}`", + AdocEscape(I.string)); } void diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.cpp b/src/lib/Gen/hbs/HandlebarsCorpus.cpp index 54ed35225f..df0a46d576 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.cpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.cpp @@ -115,149 +115,6 @@ domCreate( return corpus.toStringFn(corpus, I); } -Info const* -resolveTypedef(Corpus const& c, Info const& I) -{ - if (I.Kind == InfoKind::Typedef) - { - TypedefInfo const& TI = dynamic_cast(I); - std::unique_ptr const& T = TI.Type; - MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, &I); - NamedTypeInfo const& NT = dynamic_cast(*T); - MRDOCS_CHECK_OR(NT.Name, &I); - Info const* resolved = c.find(NT.Name->id); - MRDOCS_CHECK_OR(resolved, &I); - if (resolved->Kind == InfoKind::Typedef) - { - return resolveTypedef(c, *resolved); - } - return resolved; - } - return &I; -} - -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) -{ - // Look for the primary sibling in the parent scope - auto const* parentScope = dynamic_cast(&parent); - MRDOCS_CHECK_OR(parentScope, nullptr); - for (auto& siblingIDs = parentScope->Lookups.at(I.Name); - SymbolID const& siblingID: siblingIDs) - { - Info const* sibling = c.find(siblingID); - if (!sibling || - !shouldGenerate(*sibling) || - sibling->Name != I.Name) - { - continue; - } - bool const isPrimarySibling = visit(*sibling, [&](auto const& U) - { - if constexpr (requires { U.Template; }) - { - std::optional const& Template = U.Template; - MRDOCS_CHECK_OR(Template, false); - return !Template->Params.empty() && Template->Args.empty(); - } - return false; - }); - if (!isPrimarySibling) - { - continue; - } - return sibling; - } - return nullptr; -} - -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I); - -Info const* -findDirectPrimarySiblingWithUrl(Corpus const& c, Info const& I) -{ - // If the parent is a scope, look for a primary sibling - // in the parent scope for which we want to generate the URL - Info const* parent = c.find(I.Parent); - MRDOCS_CHECK_OR(parent, nullptr); - if (!shouldGenerate(*parent)) - { - parent = findPrimarySiblingWithUrl(c, *parent); - MRDOCS_CHECK_OR(parent, nullptr); - } - return findPrimarySiblingWithUrl(c, I, *parent); -} - -Info const* -findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) -{ -// Check if this info is a specialization or a typedef to - // a specialization, otherwise there's nothing to resolve - bool const isSpecialization = visit(I, [&](InfoTy const& U) - { - // The symbol is a specialization - if constexpr (requires { U.Template; }) - { - std::optional const& Template = U.Template; - if (Template && - !Template->Args.empty()) - { - return true; - } - } - // The symbol is a typedef to a specialization - if constexpr (std::same_as) - { - std::unique_ptr const& T = U.Type; - MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, false); - auto const& NT = dynamic_cast(*T); - MRDOCS_CHECK_OR(NT.Name, false); - MRDOCS_CHECK_OR(NT.Name->Kind == NameKind::Specialization, false); - return true; - } - return false; - }); - MRDOCS_CHECK_OR(isSpecialization, nullptr); - - // Find the parent scope containing the primary sibling - // for which we want to generate the URL - Info const* parent = c.find(I.Parent); - MRDOCS_CHECK_OR(parent, nullptr); - - // If the parent is a typedef, resolve it - // so we can iterate the members of this scope. - // We can't find siblings in a typedef because - // it's not a scope. - if (parent->Kind == InfoKind::Typedef) - { - parent = resolveTypedef(c, *parent); - MRDOCS_CHECK_OR(parent, nullptr); - } - - // If the resolved parent is also a specialization or - // a dependency for which there's no URL, we attempt to - // find the primary sibling for the parent so we take - // the URL from it. - if (!shouldGenerate(*parent)) - { - parent = findPrimarySiblingWithUrl(c, *parent); - MRDOCS_CHECK_OR(parent, nullptr); - } - - return findPrimarySiblingWithUrl(c, I, *parent); -} - -Info const* -findPrimarySiblingWithUrl(Corpus const& c, Info const& I) -{ - if (Info const* primary = findDirectPrimarySiblingWithUrl(c, I)) - { - return primary; - } - return findResolvedPrimarySiblingWithUrl(c, I); -} - } // (anon) dom::Object @@ -275,7 +132,7 @@ construct(Info const& I) const // If the URL is not available because it's a specialization // or dependency, we still want to generate the URL and anchor // for the primary template if it's part of the corpus. - if (Info const* primaryInfo = findPrimarySiblingWithUrl(getCorpus(), I)) + if (Info const* primaryInfo = findAlternativeURLInfo(getCorpus(), I)) { obj.set("url", getURL(*primaryInfo)); obj.set("anchor", names_.getQualified(primaryInfo->id, '-')); diff --git a/src/lib/Gen/hbs/VisitorHelpers.cpp b/src/lib/Gen/hbs/VisitorHelpers.cpp index ded52f0311..da3157d1cd 100644 --- a/src/lib/Gen/hbs/VisitorHelpers.cpp +++ b/src/lib/Gen/hbs/VisitorHelpers.cpp @@ -9,6 +9,11 @@ // #include "VisitorHelpers.hpp" +#include +#include +#include +#include +#include namespace clang::mrdocs::hbs { @@ -37,4 +42,207 @@ shouldGenerate(Info const& I) return true; } +namespace { + +// Resolve a typedef to its underlying Info type +Info const* +resolveTypedef(Corpus const& c, Info const& I) +{ + if (I.Kind == InfoKind::Typedef) + { + TypedefInfo const& TI = dynamic_cast(I); + std::unique_ptr const& T = TI.Type; + MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, &I); + NamedTypeInfo const& NT = dynamic_cast(*T); + MRDOCS_CHECK_OR(NT.Name, &I); + Info const* resolved = c.find(NT.Name->id); + MRDOCS_CHECK_OR(resolved, &I); + if (resolved->Kind == InfoKind::Typedef) + { + return resolveTypedef(c, *resolved); + } + return resolved; + } + return &I; +} + +/* Look for an equivalent symbol in the parent Info + */ +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) +{ + // Look for the primary sibling in the parent scope + auto const* parentScope = dynamic_cast(&parent); + MRDOCS_CHECK_OR(parentScope, nullptr); + for (auto& siblingIDs = parentScope->Lookups.at(I.Name); + SymbolID const& siblingID: siblingIDs) + { + Info const* sibling = c.find(siblingID); + if (!sibling || + !shouldGenerate(*sibling) || + sibling->Name != I.Name) + { + continue; + } + bool const isPrimarySibling = visit(*sibling, [&](auto const& U) + { + if constexpr (requires { U.Template; }) + { + std::optional const& Template = U.Template; + MRDOCS_CHECK_OR(Template, false); + return !Template->Params.empty() && Template->Args.empty(); + } + return false; + }); + if (!isPrimarySibling) + { + continue; + } + return sibling; + } + return nullptr; +} + +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I); + +/* Find the parent and look for equivalent symbol in the parent + + This function will look for the parent and, if the parent + should be generated but its member should not, we look + for an equivalent symbol in the parent. + + On the other hand, if the parent should not be generated, + we look for a symbol equivalent to the parent, and then look + for an equivalent symbol in the parent. + */ +Info const* +findDirectPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ + // If the parent is a scope, look for a primary sibling + // in the parent scope for which we want to generate the URL + Info const* parent = c.find(I.Parent); + MRDOCS_CHECK_OR(parent, nullptr); + if (!shouldGenerate(*parent)) + { + parent = findPrimarySiblingWithUrl(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + return findPrimarySiblingWithUrl(c, I, *parent); +} + +/* Resolve typedefs and look for equivalent symbol + + This function will resolve typedefs and look for an equivalent + symbol in the parent scope for which we want to generate the URL. + */ +Info const* +findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ + // Check if this info is a specialization or a typedef to + // a specialization, otherwise there's nothing to resolve + bool const isSpecialization = visit(I, [&](InfoTy const& U) + { + // The symbol is a specialization + if constexpr (requires { U.Template; }) + { + std::optional const& Template = U.Template; + if (Template && + !Template->Args.empty()) + { + return true; + } + } + // The symbol is a typedef to a specialization + if constexpr (std::same_as) + { + std::unique_ptr const& T = U.Type; + MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, false); + auto const& NT = dynamic_cast(*T); + MRDOCS_CHECK_OR(NT.Name, false); + MRDOCS_CHECK_OR(NT.Name->Kind == NameKind::Specialization, false); + return true; + } + return false; + }); + MRDOCS_CHECK_OR(isSpecialization, nullptr); + + // Find the parent scope containing the primary sibling + // for which we want to generate the URL + Info const* parent = c.find(I.Parent); + MRDOCS_CHECK_OR(parent, nullptr); + + // If the parent is a typedef, resolve it + // so we can iterate the members of this scope. + // We can't find siblings in a typedef because + // it's not a scope. + if (parent->Kind == InfoKind::Typedef) + { + parent = resolveTypedef(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + + // If the resolved parent is also a specialization or + // a dependency for which there's no URL, we attempt to + // find the primary sibling for the parent so we take + // the URL from it. + if (!shouldGenerate(*parent)) + { + parent = findPrimarySiblingWithUrl(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + + return findPrimarySiblingWithUrl(c, I, *parent); +} + +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ + if (Info const* primary = findDirectPrimarySiblingWithUrl(c, I)) + { + return primary; + } + return findResolvedPrimarySiblingWithUrl(c, I); +} + +/* Find a parent symbol whose URL we can use for I + + Unlike findPrimarySiblingWithUrl, which attempts + to find an equivalent symbol. This function will + look for a parent symbol whose URL we can use + for the specified Info and just use the parent. + + However, namespaces are not considered valid parents + for generating URLs because it would be misleading + to generate a URL for a namespace when the user + is looking for a URL for a symbol. + + */ +Info const* +findParentWithUrl(Corpus const& c, Info const& I) +{ + Info const* parent = c.find(I.Parent); + MRDOCS_CHECK_OR(parent, nullptr); + MRDOCS_CHECK_OR(!parent->isNamespace(), nullptr); + if (shouldGenerate(*parent)) + { + return parent; + } + parent = findPrimarySiblingWithUrl(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + MRDOCS_CHECK_OR(!parent->isNamespace(), nullptr); + return parent; +} +} + +Info const* +findAlternativeURLInfo(Corpus const& c, Info const& I) +{ + if (Info const* primary = findPrimarySiblingWithUrl(c, I)) + { + return primary; + } + return findParentWithUrl(c, I); +} + } // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/VisitorHelpers.hpp b/src/lib/Gen/hbs/VisitorHelpers.hpp index 87fe844e6c..faeda0bfae 100644 --- a/src/lib/Gen/hbs/VisitorHelpers.hpp +++ b/src/lib/Gen/hbs/VisitorHelpers.hpp @@ -24,6 +24,24 @@ MRDOCS_DECL bool shouldGenerate(Info const& I); +/** Find an Info type whose URL we can use for the specified Info + + When we should not generate a page for the Info as per + @ref shouldGenerate, other documentation pages might + still link to it. + + In this case, we find a related Info type whose URL + we can use for the specified Info. + + For specializations, we typically look for their primary + template. For record and enum members, we look for + the parent record or enum. For other Info types, we + return nullptr. + */ +MRDOCS_DECL +Info const* +findAlternativeURLInfo(Corpus const& c, Info const& I); + } // clang::mrdocs::hbs #endif diff --git a/src/lib/Gen/html/DocVisitor.cpp b/src/lib/Gen/html/DocVisitor.cpp index fed73952ee..af89757159 100644 --- a/src/lib/Gen/html/DocVisitor.cpp +++ b/src/lib/Gen/html/DocVisitor.cpp @@ -257,9 +257,25 @@ operator()(doc::Reference const& I) const { return (*this)(static_cast(I)); } - // AFREITAS: Unlike Adoc, we need relative URLs for HTML - fmt::format_to(std::back_inserter(dest_), "{}", - corpus_.getURL(corpus_->get(I.id)), I.string); + dom::Object symbolObj = corpus_.construct(corpus_->get(I.id)); + if (symbolObj.exists("url")) + { + std::string url = symbolObj.get("url").getString().str(); + if (url.starts_with('/')) + { + url.erase(0, 1); + } + // AFREITAS: Unlike Adoc, we need relative URLs for HTML + fmt::format_to( + std::back_inserter(dest_), + "{}", + url, HTMLEscape(I.string)); + return; + } + fmt::format_to( + std::back_inserter(dest_), + "{}", + HTMLEscape(I.string)); } void diff --git a/src/test/TestMain.cpp b/src/test/TestMain.cpp index 20342cfc12..00cfabfb2b 100644 --- a/src/test/TestMain.cpp +++ b/src/test/TestMain.cpp @@ -32,15 +32,26 @@ void DoTestAction(char const** argv) { using namespace clang::mrdocs; + std::vector testPaths( + testArgs.cmdLineInputs.begin(), + testArgs.cmdLineInputs.end()); + testArgs.cmdLineInputs.clear(); + for (auto const& inputPath: testPaths) + { + if (!files::exists(inputPath)) + { + report::warn("Path does not exist: \"{}\"", inputPath); + } + } + TestRunner runner(testArgs.generator); - for (auto const& inputPath: testArgs.cmdLineInputs) + for (auto const& inputPath: testPaths) { runner.checkPath(inputPath, argv); } auto const& results = runner.results; std::stringstream os; - switch(testArgs.action) { case Action::test: diff --git a/test-files/golden-tests/javadoc/ref.html b/test-files/golden-tests/javadoc/ref.html index f75e0582c7..fc43d744d6 100644 --- a/test-files/golden-tests/javadoc/ref.html +++ b/test-files/golden-tests/javadoc/ref.html @@ -44,10 +44,10 @@

Functions

f0 -f5

See A::f1

+f5

See A::f1

-f6

See F::operator~

+f6

See F::operator~

@@ -66,7 +66,7 @@

Types

-B

See f1

+B

See f1

C @@ -84,7 +84,7 @@

Functions

-f1

See f0

+f1

See f0

@@ -94,7 +94,7 @@

Functions

A::B

-

See f1

+

See f1

@@ -126,8 +126,8 @@

Member Functions

Description

-

See A::f1

-

See ::A::f1

+

See A::f1

+

See ::A::f1

@@ -234,7 +234,7 @@

Types

-E

See f3

+E

See f3

@@ -259,7 +259,7 @@

Member Functions

A::D::E

-

See f3

+

See f3

@@ -278,8 +278,8 @@

Synopsis

Description

-

See f4

-

See C::f4

+

See f4

+

See C::f4

@@ -304,7 +304,7 @@

Synopsis

A::f1

-

See f0

+

See f0

@@ -322,7 +322,7 @@

Synopsis

Description

-

See ::f0

+

See ::f0

@@ -1038,7 +1038,7 @@

Synopsis

f5

-

See A::f1

+

See A::f1

@@ -1056,7 +1056,7 @@

Synopsis

Description

-

See ::A::f1

+

See ::A::f1

@@ -1065,7 +1065,7 @@

Description

f6

@@ -1083,44 +1083,44 @@

Synopsis

Description

-

See F::operator,

-

See F::operator()

-

See F::operator[]

-

See F::operator+

-

See F::operator++

-

See F::operator+=

-

See F::operator&

-

See F::operator&&

-

See F::operator&=

-

See F::operator|

-

See F::operator||

-

See F::operator|=

-

See F::operator-

-

See F::operator--

-

See F::operator-=

-

See F::operator->

-

See F::operator->*

-

See F::operator<

-

See F::operator<<

-

See F::operator<<=

-

See F::operator<=

-

See F::operator<=>

-

See F::operator>

-

See F::operator>>

-

See F::operator>>=

-

See F::operator>=

-

See F::operator*

-

See F::operator*=

-

See F::operator%

-

See F::operator%=

-

See F::operator/

-

See F::operator/=

-

See F::operator^

-

See F::operator^=

-

See F::operator=

-

See F::operator==

-

See F::operator!

-

See F::operator!=

+

See F::operator,

+

See F::operator()

+

See F::operator[]

+

See F::operator+

+

See F::operator++

+

See F::operator+=

+

See F::operator&

+

See F::operator&&

+

See F::operator&=

+

See F::operator|

+

See F::operator||

+

See F::operator|=

+

See F::operator-

+

See F::operator--

+

See F::operator-=

+

See F::operator->

+

See F::operator->*

+

See F::operator<

+

See F::operator<<

+

See F::operator<<=

+

See F::operator<=

+

See F::operator<=>

+

See F::operator>

+

See F::operator>>

+

See F::operator>>=

+

See F::operator>=

+

See F::operator*

+

See F::operator*=

+

See F::operator%

+

See F::operator%=

+

See F::operator/

+

See F::operator/=

+

See F::operator^

+

See F::operator^=

+

See F::operator=

+

See F::operator==

+

See F::operator!

+

See F::operator!=