diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index a2e97b37f5..c10ff4a6cf 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -158,6 +158,17 @@ "title": "Include paths", "type": "array" }, + "inherit-base-members": { + "default": "copy-dependencies", + "description": "Determine how derived classes inherit members of base classes. When set to `never`, derived classes do not inherit members of base classes and only the relationship is stored. When set to `reference`, derived classes list members of base classes but references are still linked to the base class. When set to `copy`, a copy is created for each base symbol as if it was declared in the derived class. If the base class is a dependency, the extraction mode is copied from the new parent. When set to `copy-dependencies`, a reference is created by default and a copy is created when the base class is a dependency.", + "enum": [ + "never", + "reference", + "copy", + "copy-dependencies" + ], + "title": "Determine how derived classes inherit base members" + }, "input": { "default": [ "/." @@ -219,6 +230,16 @@ "title": "Directory or file for generating output", "type": "string" }, + "overloads": { + "default": true, + "description": "When set to `true`, MrDocs detects function overloads and groups them as a single symbol type.", + "enum": [ + true, + false + ], + "title": "Detect and group function overloads", + "type": "boolean" + }, "private-bases": { "default": true, "description": "Determine whether private base classes should be extracted", diff --git a/include/mrdocs/ADT/Optional.hpp b/include/mrdocs/ADT/Optional.hpp index d6d1540f92..60d7b4603d 100644 --- a/include/mrdocs/ADT/Optional.hpp +++ b/include/mrdocs/ADT/Optional.hpp @@ -64,10 +64,9 @@ class Optional using value_type = T; constexpr Optional() = default; - constexpr Optional( - Optional const& other) = default; - constexpr Optional& operator=( - Optional const& other) = default; + constexpr Optional(Optional const& other) = default; + constexpr Optional& operator=(Optional const& other) = default; + constexpr Optional& operator=(Optional&& other) = default; template requires std::is_constructible_v @@ -94,7 +93,7 @@ class Optional return t_; } - constexpr const value_type& value() const & noexcept + constexpr value_type const& value() const & noexcept { return t_; } @@ -104,7 +103,7 @@ class Optional return std::move(t_); } - constexpr const value_type&& value() const && noexcept + constexpr value_type const&& value() const && noexcept { return std::move(t_); } @@ -138,6 +137,8 @@ class Optional { return ! EmptyPredicate()(t_); } + + auto operator<=>(Optional const&) const = default; }; } // clang::mrdocs diff --git a/include/mrdocs/ADT/PolymorphicValue.hpp b/include/mrdocs/ADT/PolymorphicValue.hpp index 0e5155f79f..6bc99b5e97 100644 --- a/include/mrdocs/ADT/PolymorphicValue.hpp +++ b/include/mrdocs/ADT/PolymorphicValue.hpp @@ -70,7 +70,7 @@ class BadPolymorphicValueConstruction { public: BadPolymorphicValueConstruction() noexcept = default; - const char* what() const noexcept override { + char const* what() const noexcept override { return "bad polymorphic value construction"; } }; diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index 8adbd7f1f8..2dbbd6bc0b 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -132,94 +131,185 @@ class MRDOCS_VISIBLE NamespaceInfo const& globalNamespace() const noexcept; - /** Visit the members of specified Info. + /** Visit the specified Symbol IDs This function invokes the specified function `f` - for each member of the specified Info `I`. + for each member of the specified range of Symbol IDs. - For each member of `I`, the function will invoke - the function object `fn` with a type derived from - `Info` as the first argument, followed by `args...`. + For each member of `I` associated with the ID in `range`, + the function will invoke the function object `fn` with a + type derived from `Info` as the first argument, followed by + `args...`. The type of the first argument is determined by the `InfoKind` of the `Info` object. - @param I The Info to visit. - @param info The Info to visit. + @param range A range of SymbolID objects. @param f The function to invoke. @param args The arguments to pass to the function. */ - template + template R, class F, class... Args> void - traverse( - T const& I, F&& f, Args&&... args) const + traverseIDs(R&& range, F&& f, Args&&... args) const { - for (auto const& id : I.Members) + for (SymbolID const& id : range) { - visit(get(id), std::forward(f), - std::forward(args)...); + auto const* I = find(id); + MRDOCS_CHECK_OR_CONTINUE(I); + visit(*I, std::forward(f), std::forward(args)...); } } - /** Visit the members of specified Info in a stable order. + /** Options to traverse the members of an Info. + */ + struct TraverseOptions + { + /// Whether to traverse in a stable order + bool ordered = false; + /// Whether to skip inherited members whose parent is not the Info + bool skipInherited = false; + /// Whether to skip inherited members whose parent is not the Info + bool recursive = false; + }; + + /** Visit the members of specified Info. + + This function invokes the specified function `f` + for each member of the specified Info `I`. + + For each member of `I`, the function will invoke + the function object `fn` with a type derived from + `Info` as the first argument, followed by `args...`. + + The type of the first argument is determined + by the `InfoKind` of the `Info` object. + @param opts The options to traverse. @param I The Info to visit. - @param info The Info to visit. @param f The function to invoke. @param args The arguments to pass to the function. */ - template + template T, class F, class... Args> void - orderedTraverse( - T const& I, F&& f, Args&&... args) const + traverse(TraverseOptions const& opts, T const& I, F&& f, Args&&... args) const { - std::vector members(I.Members.begin(), I.Members.end()); - std::stable_sort(members.begin(), members.end(), [this](SymbolID const& lhs, SymbolID const& rhs) + if constexpr (InfoParent) { - auto const& lhsInfo = get(lhs); - auto const& rhsInfo = get(rhs); - return lhsInfo < rhsInfo; - }); - for (auto const& id : members) - { - visit(get(id), std::forward(f), - std::forward(args)...); + if (!opts.ordered) + { + if (!opts.skipInherited) + { + auto MS = allMembers(I); + traverseIDs(MS, + std::forward(f), + std::forward(args)...); + for (SymbolID const& id : MS) + { + auto const* MI = find(id); + MRDOCS_CHECK_OR_CONTINUE(MI); + traverse(opts, *MI, std::forward(f), std::forward(args)...); + } + } + else + { + auto nonInherited = + allMembers(I) | + std::views::filter([this, &I](SymbolID const& id) { + Info const* MI = find(id); + MRDOCS_CHECK_OR(MI, false); + return MI->Parent == I.id; + }); + traverseIDs(nonInherited, + std::forward(f), + std::forward(args)...); + if (opts.recursive) + { + for (SymbolID const& id : nonInherited) + { + auto const* MI = find(id); + MRDOCS_CHECK_OR_CONTINUE(MI); + traverse(opts, *MI, std::forward(f), std::forward(args)...); + } + } + } + } + else + { + auto members0 = allMembers(I); + static_assert(range_of); + std::vector members; + members.reserve(std::ranges::distance(members0)); + std::ranges::copy(members0, std::back_inserter(members)); + std::stable_sort(members.begin(), members.end(), + [this](SymbolID const& lhs, SymbolID const& rhs) + { + auto const& lhsInfo = get(lhs); + auto const& rhsInfo = get(rhs); + return lhsInfo < rhsInfo; + }); + if (!opts.skipInherited) + { + traverseIDs(members, + std::forward(f), + std::forward(args)...); + if (opts.recursive) + { + for (SymbolID const& id : members) + { + auto const* MI = find(id); + MRDOCS_CHECK_OR_CONTINUE(MI); + traverse(opts, *MI, std::forward(f), std::forward(args)...); + } + } + } + else + { + auto nonInherited = + members | + std::views::filter([this, &I](SymbolID const& id) { + Info const* MI = find(id); + MRDOCS_CHECK_OR(MI, false); + return MI->Parent == I.id; + }); + traverseIDs(nonInherited, + std::forward(f), + std::forward(args)...); + if (opts.recursive) + { + for (SymbolID const& id : nonInherited) + { + auto const* MI = find(id); + MRDOCS_CHECK_OR_CONTINUE(MI); + traverse(opts, *MI, std::forward(f), std::forward(args)...); + } + } + } + } } } - /** Visit the member overloads of specified ScopeInfo. - - This function iterates the members of the - specified ScopeInfo `S`. - - For each member in the scope, we check - if the member is a function with overloads. + /** Visit the members of specified Info. - If the member is a function with overloads, - we create an @ref OverloadSet object and invoke - the function object `f` with the @ref OverloadSet - as the first argument, followed by `args...`. + This function invokes the specified function `f` + for each member of the specified Info `I`. - If the member is not a function with overloads, - we invoke the function object `f` with the @ref Info - member as the first argument, followed by `args...`. + For each member of `I`, the function will invoke + the function object `fn` with a type derived from + `Info` as the first argument, followed by `args...`. - */ - template - void traverseOverloads( - ScopeInfo const& S, - F&& f, - Args&&... args) const; + The type of the first argument is determined + by the `InfoKind` of the `Info` object. - /** Visit the member overloads of specified ScopeInfo in stable order + @param I The Info to visit. + @param f The function to invoke. + @param args The arguments to pass to the function. */ - template - void orderedTraverseOverloads( - ScopeInfo const& S, - F&& f, - Args&&... args) const; - - //-------------------------------------------- + template T, class F, class... Args> + void + traverse(T const& I, F&& f, Args&&... args) const + { + traverse({}, I, std::forward(f), std::forward(args)...); + } /** Return the fully qualified name of the specified Info. @@ -239,7 +329,7 @@ class MRDOCS_VISIBLE std::string& temp) const; std::string - qualifiedName(const Info& I) const + qualifiedName(Info const& I) const { std::string temp; qualifiedName(I, temp); @@ -271,82 +361,11 @@ get( } } -template -void -traverseOverloadsImpl( - Corpus const& c, - std::vector const& members0, - ScopeInfo const& S, - F&& f, Args&&... args) -{ - for(const SymbolID& id : members0) - { - const Info& member = c.get(id); - const auto& members = S.Lookups.at(member.Name); - auto first_func = std::ranges::find_if( - members, [&c](const SymbolID& elem) - { - return c.get(elem).isFunction(); - }); - bool const nonOverloadedFunction = members.size() == 1; - bool const notFunction = first_func == members.end(); - if (nonOverloadedFunction || - notFunction) - { - visit(member, std::forward(f), - std::forward(args)...); - } - else if (*first_func == id) - { - OverloadSet overloads( - member.Name, - member.Parent, - members); - visit(overloads, std::forward(f), - std::forward(args)...); - } - } -} - -template -void -Corpus:: -traverseOverloads( - ScopeInfo const& S, - F&& f, Args&&... args) const -{ - MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); - return traverseOverloadsImpl( - *this, S.Members, S, std::forward(f), std::forward(args)...); - -} - -template -void -Corpus:: -orderedTraverseOverloads( - ScopeInfo const& S, - F&& f, - Args&&... args) const -{ - MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); - std::vector members(S.Members.begin(), S.Members.end()); - std::stable_sort(members.begin(), members.end(), [this](SymbolID const& lhs, SymbolID const& rhs) - { - auto const& lhsInfo = get(lhs); - auto const& rhsInfo = get(rhs); - return lhsInfo < rhsInfo; - }); - return traverseOverloadsImpl( - *this, members, S, std::forward(f), std::forward(args)...); -} - - class Corpus::iterator { - const Corpus* corpus_; - const Info* val_; - const Info*(*next_)(const Corpus*, const Info*); + Corpus const* corpus_; + Info const* val_; + Info const*(*next_)(Corpus const*, Info const*); public: using value_type = const Info; @@ -354,17 +373,17 @@ class Corpus::iterator using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; - using const_pointer = const value_type*; - using const_reference = const value_type&; + using const_pointer = value_type const*; + using const_reference = value_type const&; iterator() = default; - iterator(const iterator&) = default; - iterator& operator=(const iterator&) = default; + iterator(iterator const&) = default; + iterator& operator=(iterator const&) = default; iterator( - const Corpus* corpus, - const Info* val, - const Info*(*next)(const Corpus*, const Info*)) + Corpus const* corpus, + Info const* val, + Info const*(*next)(Corpus const*, Info const*)) : corpus_(corpus) , val_(val) , next_(next) diff --git a/src/lib/Dom/LazyArray.hpp b/include/mrdocs/Dom/LazyArray.hpp similarity index 98% rename from src/lib/Dom/LazyArray.hpp rename to include/mrdocs/Dom/LazyArray.hpp index 261ed6189e..e267017604 100644 --- a/src/lib/Dom/LazyArray.hpp +++ b/include/mrdocs/Dom/LazyArray.hpp @@ -11,11 +11,11 @@ #ifndef MRDOCS_LIB_DOM_LAZY_ARRAY_HPP #define MRDOCS_LIB_DOM_LAZY_ARRAY_HPP -#include "mrdocs/Dom.hpp" -#include "mrdocs/Platform.hpp" -#include "mrdocs/Support/Error.hpp" -#include +#include +#include +#include #include +#include namespace clang { namespace mrdocs { diff --git a/src/lib/Dom/LazyObject.hpp b/include/mrdocs/Dom/LazyObject.hpp similarity index 98% rename from src/lib/Dom/LazyObject.hpp rename to include/mrdocs/Dom/LazyObject.hpp index 4d5cc5d324..2da3ebfdd6 100644 --- a/src/lib/Dom/LazyObject.hpp +++ b/include/mrdocs/Dom/LazyObject.hpp @@ -11,14 +11,12 @@ #ifndef MRDOCS_LIB_DOM_LAZY_OBJECT_HPP #define MRDOCS_LIB_DOM_LAZY_OBJECT_HPP -#include "mrdocs/Dom.hpp" -#include "mrdocs/Platform.hpp" -#include "mrdocs/Support/Error.hpp" +#include +#include +#include #include -namespace clang { -namespace mrdocs { -namespace dom { +namespace clang::mrdocs::dom { namespace detail { @@ -451,8 +449,6 @@ LazyObject(T const& arr, Context const& context) return newObject>(arr, context); } -} // dom -} // mrdocs -} // clang +} // clang::mrdocs::dom #endif diff --git a/include/mrdocs/Dom/String.hpp b/include/mrdocs/Dom/String.hpp index 8a06a5fb92..ff690c8f57 100644 --- a/include/mrdocs/Dom/String.hpp +++ b/include/mrdocs/Dom/String.hpp @@ -31,7 +31,7 @@ concept StringLikeTy = class MRDOCS_DECL String final { - const char* ptr_ = nullptr; + char const* ptr_ = nullptr; bool is_literal() const noexcept @@ -52,7 +52,7 @@ class MRDOCS_DECL */ void construct( - const char* s, + char const* s, std::size_t n); public: @@ -79,7 +79,7 @@ class MRDOCS_DECL The newly constructed string acquries shared ownership of the string referenced by other. */ - String(const String& other) noexcept; + String(String const& other) noexcept; /** Destructor. */ @@ -116,7 +116,7 @@ class MRDOCS_DECL @param `len` The length of the string. */ String( - const char* str, + char const* str, std::size_t len) { // empty strings are stored as nullptr @@ -159,7 +159,7 @@ class MRDOCS_DECL */ String& operator=( - const String& other) noexcept + String const& other) noexcept { String temp(other); swap(temp); @@ -223,7 +223,7 @@ class MRDOCS_DECL The pointed-to character buffer returned by this function is always null-terminated. */ - const char* data() const noexcept; + char const* data() const noexcept; /** Return the string. diff --git a/include/mrdocs/Metadata.hpp b/include/mrdocs/Metadata.hpp index 765d491bd7..918fbcae9e 100644 --- a/include/mrdocs/Metadata.hpp +++ b/include/mrdocs/Metadata.hpp @@ -17,6 +17,10 @@ // All headers related to // metadata extracted from AST +#include +#include +#include +#include #include #include #include @@ -30,16 +34,11 @@ #include #include #include -#include -#include -#include -#include #include #include #include #include #include #include -#include #endif diff --git a/include/mrdocs/Metadata/DomCorpus.hpp b/include/mrdocs/Metadata/DomCorpus.hpp index 11af1e4205..d5ad1e55fb 100644 --- a/include/mrdocs/Metadata/DomCorpus.hpp +++ b/include/mrdocs/Metadata/DomCorpus.hpp @@ -14,14 +14,16 @@ #define MRDOCS_API_DOM_DOMCORPUS_HPP #include -#include #include -#include -#include #include namespace clang::mrdocs { +class Corpus; +struct Info; +struct Javadoc; +class SymbolID; + /** Front-end factory for producing Dom nodes. This class keeps a reference to the @ref Corpus @@ -83,17 +85,6 @@ class MRDOCS_DECL dom::Object construct(Info const& I) const; - /** Return a Dom value representing an overload set. - - A @ref Generator should override this member - and return suitable @ref dom::Value representing - the overload set. - */ - virtual - dom::Object - construct( - OverloadSet const& os) const; - /** Return a Dom object representing the given symbol. @return A value containing the symbol @@ -116,6 +107,12 @@ class MRDOCS_DECL getJavadoc(Javadoc const& jd) const; }; +/** Return a list of the parent symbols of the specified Info. + */ +MRDOCS_DECL +dom::Array +getParents(DomCorpus const& C, Info const& I); + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Expression.hpp b/include/mrdocs/Metadata/Expression.hpp index 444ca8c5b0..fb4996eecb 100644 --- a/include/mrdocs/Metadata/Expression.hpp +++ b/include/mrdocs/Metadata/Expression.hpp @@ -11,6 +11,7 @@ #ifndef MRDOCS_API_METADATA_EXPRESSION_HPP #define MRDOCS_API_METADATA_EXPRESSION_HPP +#include #include #include #include @@ -26,6 +27,10 @@ struct ExprInfo auto operator<=>(ExprInfo const&) const = default; }; +MRDOCS_DECL +void +merge(ExprInfo& I, ExprInfo&& Other); + /** Represents an expression with a (possibly known) value */ template struct ConstantExprInfo @@ -47,6 +52,20 @@ struct ConstantExprInfo "expression type must be integral"); }; +template +static void merge( + ConstantExprInfo& I, + ConstantExprInfo&& Other) +{ + merge( + dynamic_cast(I), + dynamic_cast(Other)); + if (!I.Value) + { + I.Value = std::move(Other.Value); + } +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index 4a022321d6..69b09de303 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -12,18 +12,18 @@ #ifndef MRDOCS_API_METADATA_INFO_HPP #define MRDOCS_API_METADATA_INFO_HPP -#include -#include -#include +#include #include +#include #include #include #include #include #include -#include +#include +#include #include - +#include namespace clang::mrdocs { @@ -54,14 +54,15 @@ void tag_invoke( dom::ValueFromTag, dom::Value& v, - InfoKind kind) + InfoKind const kind) { v = toString(kind); } /** Base class with common properties of all symbols */ -struct MRDOCS_VISIBLE Info : SourceInfo +struct MRDOCS_VISIBLE Info + : SourceInfo { /** The unique identifier for this symbol. */ @@ -113,9 +114,9 @@ struct MRDOCS_VISIBLE Info : SourceInfo //-------------------------------------------- - virtual ~Info() = default; + ~Info() override = default; - Info(Info const& Other) = delete; + Info(Info const& Other) = default; /** Move constructor. */ @@ -128,8 +129,8 @@ struct MRDOCS_VISIBLE Info : SourceInfo */ explicit Info( - InfoKind kind, - SymbolID ID) noexcept + InfoKind const kind, + SymbolID const& ID) noexcept : id(ID) , Kind(kind) { @@ -162,7 +163,7 @@ struct InfoCommonBase : Info #include protected: - constexpr explicit InfoCommonBase(SymbolID ID) + constexpr explicit InfoCommonBase(SymbolID const& ID) : Info(K, ID) { } @@ -204,6 +205,44 @@ visit( } } +/** Merges two Info objects. + + This function is used to merge two Info objects with the same SymbolID. + The function assumes that the two Info objects are of the same type. + If they are not, the function will fail. + + @param I The Info object to merge into. + @param Other The Info object to merge from. +*/ +MRDOCS_DECL +void +merge(Info& I, Info&& Other); + +/** Merges two Info objects according to the behavior of the derived class. + */ +template InfoTy> +void +merge(InfoTy& I, InfoTy&& Other) +{ + MRDOCS_ASSERT(I.Kind == Other.Kind); + MRDOCS_ASSERT(I.id == Other.id); + Info& base = I; + visit(base, [&](DerivedInfoTy& derived) mutable + { + DerivedInfoTy& otherDerived = static_cast(Other); + merge(derived, std::move(otherDerived)); + }); +} + +inline +bool +canMerge(Info const& I, Info const& Other) +{ + return + I.Kind == Other.Kind && + I.id == Other.id; +} + /** A concept for types that have `Info` members. In most cases `T` is another `Info` type that @@ -213,21 +252,104 @@ visit( However, an @ref OverloadSet is also a type that contains `Info` members without being `Info` itself. */ -template -concept InfoParent = requires(T const& t) { - { t.Members } -> std::ranges::range; - requires std::convertible_to, SymbolID const&>; +template +concept InfoParent = requires(InfoTy const& I) +{ + { allMembers(I) } -> range_of; }; -/** Return the Info to a @ref dom::Value object. +/** Map the Info to a @ref dom::Object. */ -MRDOCS_DECL +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + Info const& I, + DomCorpus const* domCorpus) +{ + MRDOCS_ASSERT(domCorpus); + io.map("id", I.id); + if (!I.Name.empty()) + { + io.map("name", I.Name); + } + io.map("kind", I.Kind); + io.map("access", I.Access); + io.map("extraction", I.Extraction); + io.map("isRegular", I.Extraction == ExtractionMode::Regular); + io.map("isSeeBelow", I.Extraction == ExtractionMode::SeeBelow); + io.map("isImplementationDefined", I.Extraction == ExtractionMode::ImplementationDefined); + io.map("isDependency", I.Extraction == ExtractionMode::Dependency); + if (I.Parent) + { + io.map("parent", I.Parent); + io.defer("parents", [&] + { + return getParents(*domCorpus, I); + }); + } + if (I.javadoc) + { + io.map("doc", *I.javadoc); + } + io.map("loc", dynamic_cast(I)); +} + +/** Map the Polymorphic Info to a @ref dom::Object. + */ +template PolymorphicInfo> +requires std::derived_from +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + PolymorphicInfo const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::LazyObjectMapTag{}, + io, + U, + domCorpus); + }); +} + +/** Return the Info as a @ref dom::Value object. + */ +inline void tag_invoke( dom::ValueFromTag, dom::Value& v, Info const& I, - DomCorpus const* domCorpus); + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** Map the Polymorphic Info as a @ref dom::Value object. + */ +template InfoTy> +requires std::derived_from +void +tag_invoke( + dom::ValueFromTag, + IO& io, + InfoTy const& I, + DomCorpus const* domCorpus) +{ + visit(*I, [&](auto const& U) + { + tag_invoke( + dom::ValueFromTag{}, + io, + U, + domCorpus); + }); +} /** Compare two Info objects */ diff --git a/include/mrdocs/Metadata/Info/Concept.hpp b/include/mrdocs/Metadata/Info/Concept.hpp index 5651efa1be..42e7627bb9 100644 --- a/include/mrdocs/Metadata/Info/Concept.hpp +++ b/include/mrdocs/Metadata/Info/Concept.hpp @@ -39,6 +39,41 @@ struct ConceptInfo final } }; +MRDOCS_DECL +void +merge(ConceptInfo& I, ConceptInfo&& Other); + +/** Map a ConceptInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + ConceptInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("template", I.Template); + if (!I.Constraint.Written.empty()) + { + io.map("constraint", I.Constraint.Written); + } +} + +/** Map the ConceptInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ConceptInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Enum.hpp b/include/mrdocs/Metadata/Info/Enum.hpp index 37ddc9e6ac..3c85171459 100644 --- a/include/mrdocs/Metadata/Info/Enum.hpp +++ b/include/mrdocs/Metadata/Info/Enum.hpp @@ -15,14 +15,14 @@ #include #include #include -#include #include +#include +#include namespace clang::mrdocs { struct EnumInfo final : InfoCommonBase - , ScopeInfo { // Indicates whether this enum is scoped (e.g. enum class). bool Scoped = false; @@ -32,6 +32,12 @@ struct EnumInfo final // this will be "short". PolymorphicValue UnderlyingType; + /** The members of this scope. + + All members are enum constants; + */ + std::vector Constants; + //-------------------------------------------- explicit EnumInfo(SymbolID ID) noexcept @@ -40,6 +46,46 @@ struct EnumInfo final } }; +inline +auto& +allMembers(EnumInfo const& T) +{ + return T.Constants; +} + +MRDOCS_DECL +void +merge(EnumInfo& I, EnumInfo&& Other); + +/** Map a EnumInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + EnumInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("type", I.UnderlyingType); + io.map("isScoped", I.Scoped); + io.map("constants", dom::LazyArray(I.Constants, domCorpus)); +} + +/** Map the EnumInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + EnumInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/EnumConstant.hpp b/include/mrdocs/Metadata/Info/EnumConstant.hpp index 9625de237b..d65c50812c 100644 --- a/include/mrdocs/Metadata/Info/EnumConstant.hpp +++ b/include/mrdocs/Metadata/Info/EnumConstant.hpp @@ -34,6 +34,40 @@ struct EnumConstantInfo final } }; +MRDOCS_DECL +void +merge(EnumConstantInfo& I, EnumConstantInfo&& Other); + +/** Map a EnumConstantInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + EnumConstantInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + if (!I.Initializer.Written.empty()) + { + io.map("initializer", I.Initializer.Written); + } +} + +/** Map the EnumConstantInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + EnumConstantInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Field.hpp b/include/mrdocs/Metadata/Info/Field.hpp index 875dad1052..56d01e3191 100644 --- a/include/mrdocs/Metadata/Info/Field.hpp +++ b/include/mrdocs/Metadata/Info/Field.hpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace clang::mrdocs { @@ -65,6 +66,52 @@ struct FieldInfo final } }; +MRDOCS_DECL +void +merge(FieldInfo& I, FieldInfo&& Other); + +/** Map a FieldInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FieldInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("type", I.Type); + if (!I.Default.Written.empty()) + { + io.map("default", I.Default.Written); + } + io.map("isMaybeUnused", I.IsMaybeUnused); + io.map("isDeprecated", I.IsDeprecated); + io.map("isVariant", I.IsVariant); + io.map("isMutable", I.IsMutable); + io.map("isBitfield", I.IsBitfield); + io.map("hasNoUniqueAddress", I.HasNoUniqueAddress); + if (I.IsBitfield) + { + io.map("bitfieldWidth", I.BitfieldWidth.Written); + } + io.map("attributes", dom::LazyArray(I.Attributes)); +} + +/** Map the FieldInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FieldInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Friend.hpp b/include/mrdocs/Metadata/Info/Friend.hpp index 4889885552..e14887159f 100644 --- a/include/mrdocs/Metadata/Info/Friend.hpp +++ b/include/mrdocs/Metadata/Info/Friend.hpp @@ -38,6 +38,50 @@ struct FriendInfo final } }; +MRDOCS_DECL +void +merge(FriendInfo& I, FriendInfo&& Other); + +/** Map a FriendInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FriendInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + if (I.FriendSymbol) + { + io.defer("name", [&I, domCorpus]{ + return dom::ValueFrom(I.FriendSymbol, domCorpus).get("name"); + }); + io.map("symbol", I.FriendSymbol); + } + else if (I.FriendType) + { + io.defer("name", [&]{ + return dom::ValueFrom(I.FriendType, domCorpus).get("name"); + }); + io.map("type", I.FriendType); + } +} + +/** Map the FriendInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FriendInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Function.hpp b/include/mrdocs/Metadata/Info/Function.hpp index 86d298a9dc..259b418f9c 100644 --- a/include/mrdocs/Metadata/Info/Function.hpp +++ b/include/mrdocs/Metadata/Info/Function.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,8 @@ struct Param , Name(std::move(name)) , Default(std::move(def_arg)) {} + + auto operator<=>(const Param&) const = default; }; /** Return the Param as a @ref dom::Value object. @@ -174,7 +177,75 @@ struct FunctionInfo final } }; -//------------------------------------------------ +MRDOCS_DECL +void +merge(FunctionInfo& I, FunctionInfo&& Other); + +/** Map a FunctionInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + FunctionInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("isVariadic", I.IsVariadic); + io.map("isVirtual", I.IsVirtual); + io.map("isVirtualAsWritten", I.IsVirtualAsWritten); + io.map("isPure", I.IsPure); + io.map("isDefaulted", I.IsDefaulted); + io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); + io.map("isDeleted", I.IsDeleted); + io.map("isDeletedAsWritten", I.IsDeletedAsWritten); + io.map("isNoReturn", I.IsNoReturn); + io.map("hasOverrideAttr", I.HasOverrideAttr); + io.map("hasTrailingReturn", I.HasTrailingReturn); + io.map("isConst", I.IsConst); + io.map("isVolatile", I.IsVolatile); + io.map("isFinal", I.IsFinal); + io.map("isNodiscard", I.IsNodiscard); + io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); + if (I.Constexpr != ConstexprKind::None) + { + io.map("constexprKind", I.Constexpr); + } + if (I.StorageClass != StorageClassKind::None) + { + io.map("storageClass", I.StorageClass); + } + if (I.RefQualifier != ReferenceKind::None) + { + io.map("refQualifier", I.RefQualifier); + } + io.map("class", I.Class); + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("return", I.ReturnType); + io.map("template", I.Template); + io.map("overloadedOperator", I.OverloadedOperator); + io.map("exceptionSpec", I.Noexcept); + io.map("explicitSpec", I.Explicit); + if (!I.Requires.Written.empty()) + { + io.map("requires", I.Requires.Written); + } + io.map("attributes", dom::LazyArray(I.Attributes)); +} + +/** Map the FunctionInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + FunctionInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} } // clang::mrdocs diff --git a/include/mrdocs/Metadata/Info/Guide.hpp b/include/mrdocs/Metadata/Info/Guide.hpp index 58481d0427..fbcc008146 100644 --- a/include/mrdocs/Metadata/Info/Guide.hpp +++ b/include/mrdocs/Metadata/Info/Guide.hpp @@ -11,9 +11,11 @@ #ifndef MRDOCS_API_METADATA_GUIDE_HPP #define MRDOCS_API_METADATA_GUIDE_HPP +#include #include #include #include +#include #include #include @@ -46,10 +48,43 @@ struct GuideInfo final explicit GuideInfo(SymbolID ID) noexcept : InfoCommonBase(ID) - { - } + {} }; +MRDOCS_DECL +void +merge(GuideInfo& I, GuideInfo&& Other); + +/** Map a GuideInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + GuideInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("params", dom::LazyArray(I.Params, domCorpus)); + io.map("deduced", I.Deduced); + io.map("template", I.Template); + io.map("explicitSpec", I.Explicit); +} + +/** Map the GuideInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + GuideInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Namespace.hpp b/include/mrdocs/Metadata/Info/Namespace.hpp index d6402ce176..8c5790b3e1 100644 --- a/include/mrdocs/Metadata/Info/Namespace.hpp +++ b/include/mrdocs/Metadata/Info/Namespace.hpp @@ -12,17 +12,96 @@ #ifndef MRDOCS_API_METADATA_NAMESPACE_HPP #define MRDOCS_API_METADATA_NAMESPACE_HPP -#include #include -#include +#include +#include +#include namespace clang::mrdocs { +/** The members of a Namespace + */ +struct NamespaceTranche { + std::vector Namespaces; + std::vector NamespaceAliases; + std::vector Typedefs; + std::vector Records; + std::vector Enums; + std::vector Functions; + std::vector Variables; + std::vector Concepts; + std::vector Guides; + std::vector Usings; +}; + +MRDOCS_DECL +void +merge(NamespaceTranche& I, NamespaceTranche&& Other); + +inline +auto +allMembers(NamespaceTranche const& T) +{ + // This is a trick to emulate views::concat in C++20 + return std::views::transform( + std::views::iota(0, 10), + [&T](int const i) -> auto const& { + switch (i) { + case 0: return T.Namespaces; + case 1: return T.NamespaceAliases; + case 2: return T.Typedefs; + case 3: return T.Records; + case 4: return T.Enums; + case 5: return T.Functions; + case 6: return T.Variables; + case 7: return T.Concepts; + case 8: return T.Guides; + case 9: return T.Usings; + default: throw std::out_of_range("Invalid index"); + } + } + ) | std::ranges::views::join; +} + +/** Map a NamespaceTranche to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + NamespaceTranche const& I, + DomCorpus const* domCorpus) +{ + io.map("namespaces", dom::LazyArray(I.Namespaces, domCorpus)); + io.map("namespaceAliases", dom::LazyArray(I.NamespaceAliases, domCorpus)); + io.map("typedefs", dom::LazyArray(I.Typedefs, domCorpus)); + io.map("records", dom::LazyArray(I.Records, domCorpus)); + io.map("enums", dom::LazyArray(I.Enums, domCorpus)); + io.map("functions", dom::LazyArray(I.Functions, domCorpus)); + io.map("variables", dom::LazyArray(I.Variables, domCorpus)); + io.map("concepts", dom::LazyArray(I.Concepts, domCorpus)); + io.map("guides", dom::LazyArray(I.Guides, domCorpus)); + io.map("usings", dom::LazyArray(I.Usings, domCorpus)); +} + +/** Map the NamespaceTranche to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NamespaceTranche const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + /** Describes a namespace. */ struct NamespaceInfo final : InfoCommonBase - , ScopeInfo { bool IsInline = false; bool IsAnonymous = false; @@ -31,14 +110,56 @@ struct NamespaceInfo final */ std::vector UsingDirectives; - //-------------------------------------------- + /** The members of this namespace. + */ + NamespaceTranche Members; - explicit NamespaceInfo(SymbolID ID) noexcept + explicit NamespaceInfo(SymbolID const &ID) noexcept : InfoCommonBase(ID) { } }; +MRDOCS_DECL +void merge(NamespaceInfo& I, NamespaceInfo&& Other); + +inline +auto +allMembers(NamespaceInfo const& T) +{ + return allMembers(T.Members); +} + +/** Map a NamespaceInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + NamespaceInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("isInline", I.IsInline); + io.map("isAnonymous", I.IsAnonymous); + io.map("members", I.Members); + io.map("usingDirectives", dom::LazyArray(I.UsingDirectives, domCorpus)); +} + +/** Map the NamespaceInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NamespaceInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp b/include/mrdocs/Metadata/Info/NamespaceAlias.hpp index 632ec7c085..632a22a00c 100644 --- a/include/mrdocs/Metadata/Info/NamespaceAlias.hpp +++ b/include/mrdocs/Metadata/Info/NamespaceAlias.hpp @@ -11,8 +11,10 @@ #ifndef MRDOCS_API_METADATA_NAMESPACEALIAS_HPP #define MRDOCS_API_METADATA_NAMESPACEALIAS_HPP +#include #include -#include +#include +#include namespace clang::mrdocs { @@ -32,6 +34,37 @@ struct NamespaceAliasInfo final } }; +MRDOCS_DECL +void +merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other); + +/** Map a NamespaceAliasInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + NamespaceAliasInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("aliasedSymbol", I.AliasedSymbol); +} + +/** Map the NamespaceAliasInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NamespaceAliasInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Overloads.hpp b/include/mrdocs/Metadata/Info/Overloads.hpp index 9994a1fc72..fceff866e4 100644 --- a/include/mrdocs/Metadata/Info/Overloads.hpp +++ b/include/mrdocs/Metadata/Info/Overloads.hpp @@ -11,78 +11,78 @@ #ifndef MRDOCS_API_METADATA_OVERLOADS_HPP #define MRDOCS_API_METADATA_OVERLOADS_HPP -#include #include +#include namespace clang::mrdocs { /** Represents a set of function overloads. - - Each `ScopeInfo`, such as `NamespaceInfo` and `RecordInfo`, - contains a list of member IDs that are part of that scope - and a lookup map of symbols with the same name that are accessible - from that scope. - - By collecting the symbols for a given name from the lookup tables - of all scopes in the namespace, we can create an `OverloadSet`. - - Besides the members of that scope, the `OverloadSet` also contains - the original namespace and the parent of the overload set. This - makes the OverloadSet similar to other `Info` classes, but it - is not part of the corpus and is not directly accessible from - the corpus: it needs to be constructed from the corpus. - */ -struct OverloadSet +struct OverloadsInfo final + : InfoCommonBase { - /// The name of the overload set. - std::string_view Name; + /// The class of the functions. + FunctionClass Class = FunctionClass::Normal; - /// The parent symbol ID. - SymbolID Parent; + /// The overloaded operator, if any. + OperatorKind OverloadedOperator = OperatorKind::None; /// The members of the overload set. - std::span Members; - - /** - * @brief Constructs an OverloadSet. - * - * @param name The name of the overload set. - * @param parent The parent symbol ID. - * @param ns The namespace of the overload set. - * @param members The members of the overload set. - */ - OverloadSet( - std::string_view name, - const SymbolID& parent, - std::span members) - : Name(name) - , Parent(parent) - , Members(members) + std::vector Members; + + //-------------------------------------------- + + explicit OverloadsInfo(SymbolID const& ID) noexcept + : InfoCommonBase(ID) { } + + explicit + OverloadsInfo(SymbolID const& Parent, std::string_view Name) noexcept; }; -template< - class Fn, - class... Args> -decltype(auto) -visit( - const OverloadSet& overloads, - Fn&& fn, - Args&&... args) +MRDOCS_DECL +void merge(OverloadsInfo& I, OverloadsInfo&& Other); + +inline +auto& +allMembers(OverloadsInfo const& T) { - return std::forward(fn)(overloads, - std::forward(args)...); + return T.Members; } MRDOCS_DECL void +addMember(OverloadsInfo& I, FunctionInfo const& Member); + +/** Map a OverloadsInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + OverloadsInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("class", I.Class); + io.map("overloadedOperator", I.OverloadedOperator); + io.map("members", dom::LazyArray(I.Members, domCorpus)); +} + +/** Map the OverloadsInfo to a @ref dom::Value object. + */ +inline +void tag_invoke( dom::ValueFromTag, dom::Value& v, - OverloadSet const& overloads, - DomCorpus const* domCorpus); + OverloadsInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} } // clang::mrdocs diff --git a/include/mrdocs/Metadata/Info/Record.hpp b/include/mrdocs/Metadata/Info/Record.hpp index 1aded0677c..d63487a398 100644 --- a/include/mrdocs/Metadata/Info/Record.hpp +++ b/include/mrdocs/Metadata/Info/Record.hpp @@ -15,14 +15,207 @@ #include #include #include -#include #include #include +#include +#include +#include #include +#include #include namespace clang::mrdocs { +/** A group of members that have the same access specifier. + + This struct represents a collection of symbols that share + the same access specifier within a record. + + It includes one vector for each info type allowed in a + record, and individual vectors for static functions, types, + and function overloads. +*/ +struct RecordTranche +{ + std::vector NamespaceAliases; + std::vector Typedefs; + std::vector Records; + std::vector Enums; + std::vector Functions; + std::vector StaticFunctions; + std::vector Variables; + std::vector StaticVariables; + std::vector Concepts; + std::vector Guides; + std::vector Friends; + std::vector Usings; +}; + +MRDOCS_DECL +void +merge(RecordTranche& I, RecordTranche&& Other); + +inline +auto +allMembers(RecordTranche const& T) +{ + // This is a trick to emulate views::concat in C++20 + return std::views::transform( + std::views::iota(0, 12), + [&T](int const i) -> auto const& + { + switch (i) { + case 0: return T.NamespaceAliases; + case 1: return T.Typedefs; + case 2: return T.Records; + case 3: return T.Enums; + case 4: return T.Functions; + case 5: return T.StaticFunctions; + case 6: return T.Variables; + case 7: return T.StaticVariables; + case 8: return T.Concepts; + case 9: return T.Guides; + case 10: return T.Friends; + case 11: return T.Usings; + default: throw std::out_of_range("Invalid index"); + } + } + ) | std::ranges::views::join; +} + +/** Map a RecordTranche to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + RecordTranche const& I, + DomCorpus const* domCorpus) +{ + io.map("namespaceAliases", dom::LazyArray(I.NamespaceAliases, domCorpus)); + io.map("typedefs", dom::LazyArray(I.Typedefs, domCorpus)); + io.map("records", dom::LazyArray(I.Records, domCorpus)); + io.map("enums", dom::LazyArray(I.Enums, domCorpus)); + io.map("functions", dom::LazyArray(I.Functions, domCorpus)); + io.map("staticFunctions", dom::LazyArray(I.StaticFunctions, domCorpus)); + io.map("variables", dom::LazyArray(I.Variables, domCorpus)); + io.map("staticVariables", dom::LazyArray(I.StaticVariables, domCorpus)); + io.map("concepts", dom::LazyArray(I.Concepts, domCorpus)); + io.map("guides", dom::LazyArray(I.Guides, domCorpus)); + io.map("friends", dom::LazyArray(I.Friends, domCorpus)); + io.map("usings", dom::LazyArray(I.Usings, domCorpus)); +} + +/** Map the RecordTranche to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordTranche const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + +/** The aggregated interface for a given struct, class, or union. + + This class represents the public, protected, and private + interfaces of a record. It is used to generate the + "interface" value of the DOM for symbols that represent + records or namespaces. + + The interface is not part of the Corpus. It is a temporary + structure generated to aggregate the symbols of a record. + This structure is provided to the user via the DOM. + + While the members of a Namespace are directly represented + with a Tranche, the members of a Record are represented + with an Interface. + +*/ +class RecordInterface +{ +public: + /** The aggregated public interfaces. + + This tranche contains all public members of a record + or namespace. + + */ + RecordTranche Public; + + /** The aggregated protected interfaces. + + This tranche contains all protected members of a record + or namespace. + + */ + RecordTranche Protected; + + /** The aggregated private interfaces. + + This tranche contains all private members of a record + or namespace. + + */ + RecordTranche Private; +}; + +MRDOCS_DECL +void +merge(RecordInterface& I, RecordInterface&& Other); + +/** Map a RecordInterface to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag, + IO& io, + RecordInterface const& I, + DomCorpus const*) +{ + io.map("public", I.Public); + io.map("protected", I.Protected); + io.map("private", I.Private); +} + +/** Map the RecordInterface to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordInterface const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + + +inline +auto +allMembers(RecordInterface const& T) +{ + // This is a trick to emulate views::concat in C++20 + return + std::views::iota(0, 3) | + std::views::transform( + [&T](int i) -> auto { + switch (i) { + case 0: return allMembers(T.Public); + case 1: return allMembers(T.Protected); + case 2: return allMembers(T.Private); + default: throw std::out_of_range("Invalid index"); + } + }) | + std::ranges::views::join; +} + /** Metadata for a direct base. */ struct BaseInfo @@ -59,7 +252,9 @@ enum class RecordKeyKind Union }; -MRDOCS_DECL dom::String toString(RecordKeyKind kind) noexcept; +MRDOCS_DECL +dom::String +toString(RecordKeyKind kind) noexcept; inline void @@ -75,7 +270,6 @@ tag_invoke( */ struct RecordInfo final : InfoCommonBase - , ScopeInfo { /** Kind of record this is (class, struct, or union). */ @@ -98,6 +292,8 @@ struct RecordInfo final */ std::vector Bases; + RecordInterface Interface; + //-------------------------------------------- explicit RecordInfo(SymbolID const& ID) noexcept @@ -123,6 +319,49 @@ getDefaultAccessString( } } +inline +auto +allMembers(RecordInfo const& T) +{ + return allMembers(T.Interface); +} + +MRDOCS_DECL +void +merge(RecordInfo& I, RecordInfo&& Other); + +/** Map a RecordInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + RecordInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("tag", I.KeyKind); + io.map("defaultAccess", getDefaultAccessString(I.KeyKind)); + io.map("isTypedef", I.IsTypeDef); + io.map("bases", dom::LazyArray(I.Bases, domCorpus)); + io.map("interface", I.Interface); + io.map("template", I.Template); +} + +/** Map the RecordInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + RecordInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Scope.hpp b/include/mrdocs/Metadata/Info/Scope.hpp deleted file mode 100644 index 04716c399f..0000000000 --- a/include/mrdocs/Metadata/Info/Scope.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_SCOPE_HPP -#define MRDOCS_API_METADATA_SCOPE_HPP - -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** Stores the members and lookups for an Info. - - Members are the symbols that are directly - contained in the scope of the Info. For instance, - the members of a namespace are the symbols - declared in the namespace. The members of a - class are the symbols and functions declared - in the class. - - The Lookups are the symbols that are accessible - from the scope of the Info. For instance, the - Lookups["foo"] of a namespace are the symbols - declared as "foo" in the namespace. - - This Info class can be used as a base class - for other Info classes, such as NamespaceInfo, - ClassInfo, that represent scopes. This class - can also be used with composition, such as in - @ref Interface to represent different scopes of - the same class (such as member and static overloads). - -*/ -struct ScopeInfo -{ - /** The members of this scope. - */ - std::vector Members; - - /** The lookup table for this scope. - */ - std::unordered_map< - std::string, - std::vector> Lookups; -}; - -/** Get a dom::Array of overloads for a scope - - This function takes a ScopeInfo, such as - Namespace or Record, and returns a dom::Array - of overloads in this scope using the DomCorpus - to resolve the SymbolIDs in this scope. - - If the symbol is not overloaded, the - symbol is included in the array. - - When the symbol is overloaded, the OverloadSet - is included in the array. - - This function makes no distinction between - overloads with different access specifiers. - - Instead, we need to traverse the overloads and - generate the data whenever the information - is requested in the handlebars templates - via the LazyObject. - */ -MRDOCS_DECL -dom::Array -generateScopeOverloadsArray( - ScopeInfo const& I, - DomCorpus const& domCorpus); - -/** Emplace a member Info into a ScopeInfo - - Given a ScopeInfo `P` and an Info `C`, this function will - add the `C.id` to `P.Members` if it's not already there, - and add the `C.id` to `P.Lookups[C.Name]` if it's not - already there. -*/ -MRDOCS_DECL -void -addMember( - ScopeInfo& P, - Info& C); - -} // clang::mrdocs - -#endif diff --git a/include/mrdocs/Metadata/Info/Specialization.hpp b/include/mrdocs/Metadata/Info/Specialization.hpp index c1c5fdf8c8..41dadb32fa 100644 --- a/include/mrdocs/Metadata/Info/Specialization.hpp +++ b/include/mrdocs/Metadata/Info/Specialization.hpp @@ -11,7 +11,10 @@ #ifndef MRDOCS_API_METADATA_SPECIALIZATION_HPP #define MRDOCS_API_METADATA_SPECIALIZATION_HPP -#include +#include +#include +#include +#include #include namespace clang::mrdocs { @@ -20,7 +23,6 @@ namespace clang::mrdocs { */ struct SpecializationInfo final : InfoCommonBase - , ScopeInfo { /** The template arguments the parent template is specialized for */ std::vector> Args; @@ -34,6 +36,37 @@ struct SpecializationInfo final } }; +MRDOCS_DECL +void +merge(SpecializationInfo& I, SpecializationInfo&& Other); + +/** Map a SpecializationInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + SpecializationInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); +} + +/** Map the SpecializationInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + SpecializationInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Typedef.hpp b/include/mrdocs/Metadata/Info/Typedef.hpp index 3623e5acdb..7e1d148c10 100644 --- a/include/mrdocs/Metadata/Info/Typedef.hpp +++ b/include/mrdocs/Metadata/Info/Typedef.hpp @@ -13,7 +13,7 @@ #ifndef MRDOCS_API_METADATA_TYPEDEF_HPP #define MRDOCS_API_METADATA_TYPEDEF_HPP -#include +#include #include #include @@ -49,6 +49,39 @@ struct TypedefInfo final } }; +MRDOCS_DECL +void +merge(TypedefInfo& I, TypedefInfo&& Other); + +/** Map a TypedefInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + TypedefInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("type", I.Type); + io.map("template", I.Template); + io.map("isUsing", I.IsUsing); +} + +/** Map the TypedefInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + TypedefInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Using.hpp b/include/mrdocs/Metadata/Info/Using.hpp similarity index 70% rename from include/mrdocs/Metadata/Using.hpp rename to include/mrdocs/Metadata/Info/Using.hpp index 90b2019674..f7d54f0b74 100644 --- a/include/mrdocs/Metadata/Using.hpp +++ b/include/mrdocs/Metadata/Info/Using.hpp @@ -15,8 +15,9 @@ #include #include #include +#include +#include #include -#include namespace clang::mrdocs { @@ -78,6 +79,38 @@ struct UsingInfo final } }; +MRDOCS_DECL +void merge(UsingInfo& I, UsingInfo&& Other); + +/** Map a UsingInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + UsingInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("class", I.Class); + io.map("shadows", dom::LazyArray(I.UsingSymbols, domCorpus)); + io.map("qualifier", I.Qualifier); +} + +/** Map the UsingInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + UsingInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Info/Variable.hpp b/include/mrdocs/Metadata/Info/Variable.hpp index 73959930d1..36ef9ff090 100644 --- a/include/mrdocs/Metadata/Info/Variable.hpp +++ b/include/mrdocs/Metadata/Info/Variable.hpp @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include namespace clang::mrdocs { @@ -54,6 +56,51 @@ struct VariableInfo final } }; +MRDOCS_DECL +void +merge(VariableInfo& I, VariableInfo&& Other); + +/** Map a VariableInfo to a dom::Object. + */ +template +void +tag_invoke( + dom::LazyObjectMapTag t, + IO& io, + VariableInfo const& I, + DomCorpus const* domCorpus) +{ + tag_invoke(t, io, dynamic_cast(I), domCorpus); + io.map("type", I.Type); + io.map("template", I.Template); + if (I.StorageClass != StorageClassKind::None) + { + io.map("storageClass", I.StorageClass); + } + io.map("isInline", I.IsInline); + io.map("isConstexpr", I.IsConstexpr); + io.map("isConstinit", I.IsConstinit); + io.map("isThreadLocal", I.IsThreadLocal); + if (!I.Initializer.Written.empty()) + { + io.map("initializer", I.Initializer.Written); + } + io.map("attributes", dom::LazyArray(I.Attributes)); +} + +/** Map the VariableInfo to a @ref dom::Value object. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + VariableInfo const& I, + DomCorpus const* domCorpus) +{ + v = dom::LazyObject(I, domCorpus); +} + } // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Interface.hpp b/include/mrdocs/Metadata/Interface.hpp deleted file mode 100644 index 73c85efa67..0000000000 --- a/include/mrdocs/Metadata/Interface.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_API_METADATA_INTERFACE_HPP -#define MRDOCS_API_METADATA_INTERFACE_HPP - -#include -#include -#include -#include - -namespace clang::mrdocs { - -/** A group of children that have the same access specifier. - - This struct represents a collection of symbols that share - the same access specifier within a scope. - - It includes one vector for each info type, - and individual vectors for static functions, types, - and overloads. - - The tranche is not part of the Corpus. It is a temporary - structure generated to aggregate the symbols of a scope. This - structure is provided to the user via the DOM. -*/ -struct Tranche -{ - #define INFO(Type) std::vector Type; - #include - - /// The types with the same access specifier in a scope. - std::vector Types; - - /// The static functions with the same access specifier in a scope. - std::vector StaticFunctions; - - /// The overloads with the same access specifier in a scope. - ScopeInfo Overloads; - - /// The static overloads with the same access specifier in a scope. - ScopeInfo StaticOverloads; -}; - -/** Return a tranche representing the members of a namespace. - - @return The tranche. - - @param Namespace The namespace to build the tranche for. - @param corpus The complete metadata. -*/ -MRDOCS_DECL -Tranche -makeTranche( - NamespaceInfo const& Namespace, - Corpus const& corpus); - -/** Return the Tranche as a @ref dom::Value object. - */ -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - std::shared_ptr const& sp, - DomCorpus const* domCorpus); - -/** The aggregated interface for a given struct, class, or union. - - This class represents the public, protected, and private - interfaces of a record. It is used to generate the - "interface" value of the DOM for symbols that represent - records or namespaces. - - The interface is not part of the Corpus. It is a temporary - structure generated to aggregate the symbols of a record. - This structure is provided to the user via the DOM. - - While the members of a Namespace are directly represented - with a Tranche, the members of a Record are represented - with an Interface. - -*/ -class Interface -{ -public: - /// The corpus containing the complete metadata. - Corpus const& corpus; - - /** The aggregated public interfaces. - - This tranche contains all public members of a record - or namespace. - - */ - std::shared_ptr Public; - - /** The aggregated protected interfaces. - - This tranche contains all protected members of a record - or namespace. - - */ - std::shared_ptr Protected; - - /** The aggregated private interfaces. - - This tranche contains all private members of a record - or namespace. - - */ - std::shared_ptr Private; - - /** Creates an Interface object for a given record. - - @param I The record to create the interface for. - @param corpus The complete metadata. - @return The interface. - */ - MRDOCS_DECL - friend - Interface - makeInterface( - RecordInfo const& I, - Corpus const& corpus); - -private: - explicit Interface(Corpus const&) noexcept; -}; - -/** Return the composite interface for a record. - - @return The interface. - - @param I The interface to store the results in. - @param corpus The complete metadata. -*/ -MRDOCS_DECL -Interface -makeInterface( - RecordInfo const& I, - Corpus const& corpus); - -/** Return the Tranche as a @ref dom::Value object. - */ -MRDOCS_DECL -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - std::shared_ptr const& sp, - DomCorpus const* domCorpus); - -} // mrdocs::clang - -#endif diff --git a/include/mrdocs/Metadata/Javadoc.hpp b/include/mrdocs/Metadata/Javadoc.hpp index 506ff38fd0..30def3ca14 100644 --- a/include/mrdocs/Metadata/Javadoc.hpp +++ b/include/mrdocs/Metadata/Javadoc.hpp @@ -198,8 +198,8 @@ struct MRDOCS_DECL return ! isBlock(); } - bool operator==(const Node&)const noexcept = default; - virtual bool equals(const Node& other) const noexcept + bool operator==(Node const&)const noexcept = default; + virtual bool equals(Node const& other) const noexcept { return kind == other.kind; } @@ -236,11 +236,11 @@ struct Text : Node return false; } - bool operator==(const Text&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Text const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } protected: @@ -273,7 +273,7 @@ struct Styled final : Text bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -295,11 +295,11 @@ struct Link : Text { } - bool operator==(const Link&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Link const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -318,11 +318,11 @@ struct Reference : Text { } - bool operator==(const Reference&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Reference const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } protected: @@ -354,7 +354,7 @@ struct Copied : Reference bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -385,7 +385,7 @@ struct MRDOCS_DECL return children.empty(); } - bool operator==(const Block& other) const noexcept + bool operator==(Block const& other) const noexcept { if (kind != other.kind) { @@ -393,7 +393,7 @@ struct MRDOCS_DECL } return std::equal(children.begin(), children.end(), other.children.begin(), other.children.end(), - [](const auto& a, const auto& b) + [](auto const& a, auto const& b) { return a->equals(*b); }); @@ -402,7 +402,7 @@ struct MRDOCS_DECL bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } template T> @@ -446,10 +446,10 @@ struct Heading : Block } bool operator==(Heading const&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -464,11 +464,11 @@ struct Paragraph : Block { } - bool operator==(const Paragraph&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Paragraph const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } protected: @@ -492,11 +492,11 @@ struct Brief : Paragraph { } - bool operator==(const Brief&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Brief const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -518,11 +518,11 @@ struct Admonition : Paragraph { } - bool operator==(const Admonition&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Admonition const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -540,11 +540,11 @@ struct Code : Paragraph { } - bool operator==(const Code&) const noexcept = default; - bool equals(const Node& other) const noexcept override + bool operator==(Code const&) const noexcept = default; + bool equals(Node const& other) const noexcept override { return kind == other.kind && - *this == dynamic_cast(other); + *this == dynamic_cast(other); } }; @@ -559,12 +559,12 @@ struct ListItem final : Paragraph {} bool - operator==(const ListItem&) const noexcept = default; + operator==(ListItem const&) const noexcept = default; bool - equals(const Node& other) const noexcept override + equals(Node const& other) const noexcept override { - auto* p = dynamic_cast(&other); + auto* p = dynamic_cast(&other); if (!p) { return false; @@ -591,10 +591,10 @@ struct UnorderedList final : Paragraph } bool - operator==(const UnorderedList&) const noexcept = default; + operator==(UnorderedList const&) const noexcept = default; bool - equals(const Node& other) const noexcept override + equals(Node const& other) const noexcept override { auto* p = dynamic_cast(&other); if (!p) @@ -1079,6 +1079,21 @@ struct MRDOCS_DECL emplace_back(PolymorphicValue); }; +inline +void merge(Javadoc& I, Javadoc&& other) +{ + // FIXME: this doesn't merge parameter information; + // parameters with the same name but different direction + // or descriptions end up being duplicated + if(other != I) + { + // Unconditionally extend the blocks + // since each decl may have a comment. + I.append(std::move(other)); + } +} + + /** Return the Javadoc as a @ref dom::Value. */ MRDOCS_DECL diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 68e2d06780..762e9ea47d 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -167,7 +167,7 @@ operator==(PolymorphicValue const& lhs, PolymorphicValue con MRDOCS_DECL std::string -toString(const NameInfo& N); +toString(NameInfo const& N); MRDOCS_DECL void diff --git a/include/mrdocs/Metadata/Source.hpp b/include/mrdocs/Metadata/Source.hpp index 95e8254553..74d05df054 100644 --- a/include/mrdocs/Metadata/Source.hpp +++ b/include/mrdocs/Metadata/Source.hpp @@ -82,6 +82,8 @@ struct MRDOCS_DECL , Documented(documented) { } + + auto operator<=>(Location const&) const = default; }; MRDOCS_DECL @@ -130,6 +132,14 @@ struct MRDOCS_DECL SourceInfo() = default; }; +MRDOCS_DECL +void +merge(SourceInfo& I, SourceInfo const& Other); + +MRDOCS_DECL +void +merge(SourceInfo& I, SourceInfo&& Other); + void tag_invoke( dom::ValueFromTag, diff --git a/include/mrdocs/Metadata/Specifiers.hpp b/include/mrdocs/Metadata/Specifiers.hpp index 01c00bac80..bf884b880d 100644 --- a/include/mrdocs/Metadata/Specifiers.hpp +++ b/include/mrdocs/Metadata/Specifiers.hpp @@ -245,7 +245,7 @@ tag_invoke( MRDOCS_DECL dom::String toString( - const ExplicitInfo& info, + ExplicitInfo const& info, bool resolved = false, bool implicit = false); diff --git a/include/mrdocs/Metadata/Symbols.hpp b/include/mrdocs/Metadata/Symbols.hpp index 2f1088cef5..0cb24e29e2 100644 --- a/include/mrdocs/Metadata/Symbols.hpp +++ b/include/mrdocs/Metadata/Symbols.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include namespace clang::mrdocs { @@ -55,7 +56,7 @@ class SymbolID @param src The string to construct from. */ template Char> - constexpr SymbolID(const Char* src) + constexpr SymbolID(Char const* src) { int i = 0; for(value_type& c : data_) @@ -66,6 +67,12 @@ class SymbolID MRDOCS_ASSERT(i == 20); } + /** Construct a SymbolID by hashing a string + */ + static + SymbolID + createFromString(std::string_view input); + /** Return true if this is a valid SymbolID. */ explicit operator bool() const noexcept @@ -109,13 +116,13 @@ class SymbolID operator std::string_view() const noexcept { return {reinterpret_cast< - const char*>(data()), size()}; + char const*>(data()), size()}; } /** Compare two SymbolIDs with strong ordering. */ auto operator<=>( - const SymbolID& other) const noexcept + SymbolID const& other) const noexcept { return std::memcmp( data(), @@ -126,7 +133,7 @@ class SymbolID /** Compare two SymbolIDs for equality. */ bool operator==( - const SymbolID& other) const noexcept = default; + SymbolID const& other) const noexcept = default; }; /** The invalid Symbol ID. @@ -141,6 +148,12 @@ constexpr inline SymbolID SymbolID::global = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; +/** Convert a SymbolID to a string + */ +MRDOCS_DECL +std::string +toBase16Str(SymbolID const& id); + /** Return the result of comparing s0 to s1. This function returns true if the string diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index ce4e372180..02c7a55eac 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -166,7 +166,7 @@ operator==(PolymorphicValue const& lhs, PolymorphicValue const& rhs) MRDOCS_DECL std::string -toString(const TArg& arg) noexcept; +toString(TArg const& arg) noexcept; MRDOCS_DECL void @@ -434,6 +434,8 @@ struct TemplateInfo } return TemplateSpecKind::Partial; } + + auto operator<=>(TemplateInfo const&) const = default; }; MRDOCS_DECL diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index 5973443bd3..553cefea7f 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -441,7 +441,7 @@ operator==(PolymorphicValue const& lhs, PolymorphicValue con MRDOCS_DECL std::string toString( - const TypeInfo& T, + TypeInfo const& T, std::string_view Name = ""); } // clang::mrdocs diff --git a/include/mrdocs/MetadataFwd.hpp b/include/mrdocs/MetadataFwd.hpp index 5c55014c14..3ac13094e7 100644 --- a/include/mrdocs/MetadataFwd.hpp +++ b/include/mrdocs/MetadataFwd.hpp @@ -67,8 +67,6 @@ struct MemberRecord; struct MemberType; struct StaticDataMember; -struct OverloadSet; - } // clang::mrdocs #endif diff --git a/include/mrdocs/Support/Assert.hpp b/include/mrdocs/Support/Assert.hpp index dc2e144766..5cd895fe7b 100644 --- a/include/mrdocs/Support/Assert.hpp +++ b/include/mrdocs/Support/Assert.hpp @@ -32,8 +32,8 @@ namespace mrdocs { void assert_failed( - const char* msg, - const char* file, + char const* msg, + char const* file, std::uint_least32_t line); #define MRDOCS_ASSERT(x) static_cast(!! (x) || \ diff --git a/include/mrdocs/Support/Concepts.hpp b/include/mrdocs/Support/Concepts.hpp index 1495ddace8..9f0ebf9c96 100644 --- a/include/mrdocs/Support/Concepts.hpp +++ b/include/mrdocs/Support/Concepts.hpp @@ -15,9 +15,29 @@ namespace clang::mrdocs { -template +/** Concept to check if a type is a range of T */ +template concept range_of = std::ranges::range && std::same_as, T>; +/** Concept to check if a type is representing a polymorphic storage + + This concept checks if a type is used to store derived objects + of a base class. + + Examples of such types are std::unique_ptr, std::shared_ptr, + PolymorphicValue, etc. + + The `get()` function might not always be available, but + `operator*` and `operator->` should be available and return + a reference to the Base class. + */ +template +concept polymorphic_storage_for = requires(T const& t) +{ + { *t } -> std::convertible_to; + { t.operator->() } -> std::convertible_to; +}; + } // namespace clang::mrdocs diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index 475f1ea823..d4c9443a1a 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -289,12 +289,12 @@ class BadExpectedAccess : public std::exception protected: BadExpectedAccess() noexcept = default; - BadExpectedAccess(const BadExpectedAccess&) = default; + BadExpectedAccess(BadExpectedAccess const&) = default; BadExpectedAccess(BadExpectedAccess&&) = default; BadExpectedAccess& - operator=(const BadExpectedAccess&) = default; + operator=(BadExpectedAccess const&) = default; BadExpectedAccess& operator=(BadExpectedAccess&&) = default; @@ -302,7 +302,7 @@ class BadExpectedAccess : public std::exception ~BadExpectedAccess() override = default; public: [[nodiscard]] - const char* + char const* what() const noexcept override { return "bad access to Expected without Expected value"; @@ -325,7 +325,7 @@ class BadExpectedAccess : public BadExpectedAccess { } [[nodiscard]] - const E& + E const& error() const & noexcept { return unex_; @@ -339,7 +339,7 @@ class BadExpectedAccess : public BadExpectedAccess { } [[nodiscard]] - const E&& + E const&& error() const && noexcept { return std::move(unex_); @@ -395,7 +395,7 @@ class Unexpected public: constexpr - Unexpected(const Unexpected&) = default; + Unexpected(Unexpected const&) = default; constexpr Unexpected(Unexpected&&) = default; @@ -430,11 +430,11 @@ class Unexpected : unex_(il, std::forward(args)...) {} - constexpr Unexpected& operator=(const Unexpected&) = default; + constexpr Unexpected& operator=(Unexpected const&) = default; constexpr Unexpected& operator=(Unexpected&&) = default; [[nodiscard]] - constexpr const E& + constexpr E const& error() const & noexcept { return unex_; @@ -448,7 +448,7 @@ class Unexpected } [[nodiscard]] - constexpr const E&& + constexpr E const&& error() const && noexcept { return std::move(unex_); @@ -475,7 +475,7 @@ class Unexpected friend constexpr bool - operator==(const Unexpected& x, const Unexpected& y) + operator==(Unexpected const& x, const Unexpected& y) { return x.unex_ == y.error(); } @@ -516,6 +516,10 @@ namespace detail { return !t; } + else + { + return false; + } } template @@ -599,6 +603,13 @@ namespace detail # define MRDOCS_CHECK_GET_OR_MACRO(_1, _2, NAME, ...) NAME # define MRDOCS_CHECK_OR(...) \ MRDOCS_CHECK_GET_OR_MACRO(__VA_ARGS__, MRDOCS_CHECK_OR_VALUE, MRDOCS_CHECK_OR_VOID)(__VA_ARGS__) + +# define MRDOCS_CHECK_OR_CONTINUE(var) \ + if (detail::failed(var)) { \ + continue; \ + } \ + void(0) + #endif @@ -630,9 +641,9 @@ namespace detail } } - ExpectedGuard(const ExpectedGuard&) = delete; + ExpectedGuard(ExpectedGuard const&) = delete; - ExpectedGuard& operator=(const ExpectedGuard&) = delete; + ExpectedGuard& operator=(ExpectedGuard const&) = delete; constexpr T&& release() noexcept @@ -784,15 +795,15 @@ class Expected template requires - std::is_constructible_v && - std::is_constructible_v && + std::is_constructible_v && + std::is_constructible_v && (!constructible_from_expected) constexpr - explicit(explicit_conv) + explicit(explicit_conv) Expected(const Expected& x) noexcept( - std::is_nothrow_constructible_v && - std::is_nothrow_constructible_v) + std::is_nothrow_constructible_v && + std::is_nothrow_constructible_v) : has_value_(x.has_value_) { if (has_value_) @@ -843,11 +854,11 @@ class Expected { } template - requires std::is_constructible_v + requires std::is_constructible_v constexpr - explicit(!std::is_convertible_v) + explicit(!std::is_convertible_v) Expected(const Unexpected& u) - noexcept(std::is_nothrow_constructible_v) + noexcept(std::is_nothrow_constructible_v) : unex_(u.error()) , has_value_(false) { } @@ -1002,7 +1013,7 @@ class Expected template requires - std::is_constructible_v && + std::is_constructible_v && std::is_assignable_v && (std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || diff --git a/include/mrdocs/Support/Handlebars.hpp b/include/mrdocs/Support/Handlebars.hpp index 04bda644a3..de48808a5e 100644 --- a/include/mrdocs/Support/Handlebars.hpp +++ b/include/mrdocs/Support/Handlebars.hpp @@ -473,7 +473,7 @@ namespace detail { // Heterogeneous lookup support struct string_hash { using is_transparent [[maybe_unused]] = void; - size_t operator()(const char *txt) const { + size_t operator()(char const*txt) const { return std::hash{}(txt); } size_t operator()(std::string_view txt) const { diff --git a/include/mrdocs/Support/Visitor.hpp b/include/mrdocs/Support/Visitor.hpp index 5a4d9745c3..6e84599ad6 100644 --- a/include/mrdocs/Support/Visitor.hpp +++ b/include/mrdocs/Support/Visitor.hpp @@ -15,8 +15,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** A visitor for a type @@ -74,8 +73,7 @@ class Visitor @tparam Derived The derived type to visit @return The result of calling the function object */ - template> Derived> + template > Derived> decltype(auto) visit() { @@ -121,7 +119,6 @@ makeVisitor( std::forward(args)...); } -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/include/mrdocs/Support/source_location.hpp b/include/mrdocs/Support/source_location.hpp index 984a7a001b..7b98535a86 100644 --- a/include/mrdocs/Support/source_location.hpp +++ b/include/mrdocs/Support/source_location.hpp @@ -36,8 +36,8 @@ constexpr source_location current( - const char* const file = __builtin_FILE(), - const char* const function = __builtin_FUNCTION(), + char const* const file = __builtin_FILE(), + char const* const function = __builtin_FUNCTION(), const std::uint_least32_t line = __builtin_LINE(), const std::uint_least32_t column = __builtin_COLUMN()) noexcept @@ -51,13 +51,13 @@ } constexpr - const char* + char const* file_name() const noexcept { return file_; } constexpr - const char* + char const* function_name() const noexcept { return function_; @@ -77,8 +77,8 @@ } private: - const char* file_ = ""; - const char* function_ = ""; + char const* file_ = ""; + char const* function_ = ""; std::uint_least32_t line_ = 0; std::uint_least32_t column_ = 0; }; diff --git a/include/mrdocs/mrdocs.natvis b/include/mrdocs/mrdocs.natvis index d63d76b598..29ef325ba0 100644 --- a/include/mrdocs/mrdocs.natvis +++ b/include/mrdocs/mrdocs.natvis @@ -25,6 +25,7 @@ + @@ -43,6 +44,7 @@ (NamespaceInfo*)this,view(noinherit) (RecordInfo*)this,view(noinherit) *(FunctionInfo*)this,view(noinherit) + *(OverloadsInfo*)this,view(noinherit) (EnumInfo*)this,view(noinherit) (TypedefInfo*)this,view(noinherit) (VariableInfo*)this,view(noinherit) diff --git a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs index 420edff494..d845943c94 100644 --- a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs @@ -31,8 +31,8 @@ {{> symbol/signatures symbol }} {{/unless}} -{{! Tranches }} -{{#if symbol.interface}} +{{! Members }} +{{! Record interface }} {{#if (eq symbol.kind "record")}} {{#with symbol.interface}} {{>symbol/tranche tranche=public label="" is-namespace=false}} @@ -42,24 +42,24 @@ {{>symbol/tranche tranche=private label="Private" is-namespace=false}} {{/with}} -{{else}} -{{>symbol/tranche tranche=symbol.interface label="" is-namespace=true}} +{{! Namespace members }} +{{else if (eq symbol.kind "namespace")}} +{{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} -{{/if}} -{{else if (and symbol.members (ne symbol.kind "overloads"))}} -{{! Members }} +{{! Enum members }} +{{else if (eq symbol.kind "enum")}} {{#> markup/dynamic-level-h }}Members{{/markup/dynamic-level-h}} [,cols=2] |=== |Name |Description -{{#each (filter_by symbol.members "isRegular" "isSeeBelow")}} +{{#each (filter_by symbol.constants "isRegular" "isSeeBelow")}} {{#if (ne kind "enum-constant")}} |xref:{{{anchor}}}[`{{>symbol/name . nolink=true}}`] {{else}} |`{{>symbol/name . nolink=true}}` {{/if}} -|{{{~doc.brief}}} +|{{{~ doc.brief }}} {{/each}} |=== diff --git a/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs b/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs index cb63c3a183..9303de9d69 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs @@ -14,21 +14,24 @@ See: https://mrdocs.com/docs/mrdocs/develop/generators.html#dom_reference --}} +{{#if is-namespace}} {{>symbol/members-table members=tranche.namespaces title="Namespaces"}} -{{>symbol/members-table members=tranche.namespace-aliases title="Namespace Aliases"}} -{{>symbol/members-table members=(concat tranche.records tranche.types) title=(concat (select label (concat label " ") "") "Types")}} +{{>symbol/members-table members=tranche.namespaceAliases title="Namespace Aliases"}} +{{>symbol/members-table members=(concat tranche.records tranche.typedefs) title=(concat (select label (concat label " ") "") "Types")}} {{>symbol/members-table members=tranche.enums title=(concat (select label (concat label " ") "") "Enums")}} -{{#if is-namespace}} -{{>symbol/members-table members=tranche.overloads title="Functions"}} +{{>symbol/members-table members=tranche.functions title="Functions"}} {{>symbol/members-table members=tranche.variables title="Variables"}} {{>symbol/members-table members=tranche.concepts title="Concepts"}} +{{>symbol/members-table members=tranche.guides title=(concat (select label (concat label " ") "") "Deduction Guides")}} {{else}} -{{>symbol/members-table members=tranche.overloads title=(concat (select label (concat label " ") "") "Member Functions")}} -{{>symbol/members-table members=tranche.staticoverloads title=(concat (select label (concat label " ") "") "Static Member Functions")}} -{{>symbol/members-table members=tranche.fields title=(concat (select label (concat label " ") "") "Data Members")}} -{{>symbol/members-table members=tranche.variables title=(concat (select label (concat label " ") "") "Static Data Members")}} +{{>symbol/members-table members=tranche.namespaceAliases title="Namespace Aliases"}} +{{>symbol/members-table members=(concat tranche.records tranche.typedefs) title=(concat (select label (concat label " ") "") "Types")}} +{{>symbol/members-table members=tranche.enums title=(concat (select label (concat label " ") "") "Enums")}} +{{>symbol/members-table members=tranche.functions title=(concat (select label (concat label " ") "") "Member Functions")}} +{{>symbol/members-table members=tranche.staticFunctions title=(concat (select label (concat label " ") "") "Static Member Functions")}} +{{>symbol/members-table members=tranche.variables title=(concat (select label (concat label " ") "") "Data Members")}} +{{>symbol/members-table members=tranche.staticVariables title=(concat (select label (concat label " ") "") "Static Data Members")}} {{>symbol/members-table members=tranche.friends title=(concat (select label (concat label " ") "") "Friends")}} {{>symbol/members-table members=tranche.aliases title=(concat (select label (concat label " ") "") "Aliases")}} {{>symbol/members-table members=tranche.usings title=(concat (select label (concat label " ") "") "Using Declarations")}} {{/if}} -{{>symbol/members-table members=tranche.guides title=(concat (select label (concat label " ") "") "Deduction Guides")}} diff --git a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs index 13024a958d..e87858e231 100644 --- a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs @@ -39,8 +39,8 @@ {{/unless}} -{{! Tranches }} -{{#if symbol.interface}} +{{! Members }} +{{! Record interface }} {{#if (eq symbol.kind "record")}} {{#with symbol.interface}} {{>symbol/tranche tranche=public label="" is-namespace=false}} @@ -49,12 +49,11 @@ {{>symbol/tranche tranche=private label="Private" is-namespace=false}} {{/with}} -{{else}} -{{>symbol/tranche tranche=symbol.interface label="" is-namespace=true}} -{{/if}} -{{else if symbol.members}} -{{! Members }} -{{#if (and symbol.members (ne symbol.kind "overloads"))}} +{{! Namespace members }} +{{else if (eq symbol.kind "namespace")}} +{{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} +{{! Enum members }} +{{else if (eq symbol.kind "enum")}}
{{#> markup/dynamic-level-h level=2 }}Members{{/markup/dynamic-level-h}} @@ -65,7 +64,7 @@ -{{#each (filter_by symbol.members "isRegular" "isSeeBelow")}} +{{#each (filter_by symbol.constants "isRegular" "isSeeBelow")}} {{#if (ne kind "enum-constant")}} @@ -79,7 +78,6 @@
{{>symbol/name . nolink=true}}
{{/if}} -{{/if}} {{! Using directives }} {{#if symbol.usingDirectives}}
diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 5b02b8a874..eb86623f23 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -18,6 +18,7 @@ #include "lib/AST/TypeInfoBuilder.hpp" #include "lib/Support/Path.hpp" #include "lib/Support/Debug.hpp" +#include "lib/Support/Radix.hpp" #include "lib/Lib/Diagnostics.hpp" #include #include @@ -133,7 +134,7 @@ traverse(DeclTy const* D) traverseMembers(I, D); // Traverse the parents of the declaration in dependency mode. - traverseParents(I, D); + traverseParent(I, D); return &I; } @@ -253,7 +254,7 @@ template < requires (!std::derived_from) void ASTVisitor:: -traverseParents(InfoTy& I, DeclTy const* DC) +traverseParent(InfoTy& I, DeclTy const* DC) { MRDOCS_SYMBOL_TRACE(DC, context_); if (Decl const* PD = getParent(DC)) @@ -261,8 +262,7 @@ traverseParents(InfoTy& I, DeclTy const* DC) MRDOCS_SYMBOL_TRACE(PD, context_); // Check if we haven't already extracted or started - // to extract the parent scope - + // to extract the parent scope: // Traverse the parent scope as a dependency if it // hasn't been extracted yet Info* PI = nullptr; @@ -276,10 +276,14 @@ traverseParents(InfoTy& I, DeclTy const* DC) // If we found the parent scope, set it as the parent I.Parent = PI->id; - if (auto* SI = dynamic_cast(PI)) + + visit(*PI, [&](ParentInfoTy& PU) -> void { - addMember(*SI, I); - } + if constexpr (InfoParent) + { + addMember(PU, I); + } + }); } } @@ -288,15 +292,15 @@ template < std::derived_from DeclTy> void ASTVisitor:: -traverseParents(InfoTy& I, DeclTy const* D) +traverseParent(InfoTy& I, DeclTy const* D) { - traverseParents(I, D->getTemplatedDecl()); + traverseParent(I, D->getTemplatedDecl()); } Expected> ASTVisitor:: -generateUSR(const Decl* D) const +generateUSR(Decl const* D) const { MRDOCS_ASSERT(D); llvm::SmallString<128> res; @@ -315,7 +319,7 @@ generateUSR(const Decl* D) const // Handling UsingDecl if (auto const* UD = dyn_cast(D)) { - for (const auto* shadow : UD->shadows()) + for (auto const* shadow : UD->shadows()) { if (index::generateUSRForDecl(shadow->getTargetDecl(), res)) { @@ -421,8 +425,8 @@ generateUSR(const Decl* D) const if (index::generateUSRForDecl(D, res)) return Unexpected(Error("Failed to generate USR")); - const auto* Described = dyn_cast_if_present(D); - const auto* Templated = D; + auto const* Described = dyn_cast_if_present(D); + auto const* Templated = D; if (auto const* DT = D->getDescribedTemplate()) { Described = DT; @@ -434,8 +438,8 @@ generateUSR(const Decl* D) const if(Described) { - const TemplateParameterList* TPL = Described->getTemplateParameters(); - if(const auto* RC = TPL->getRequiresClause()) + TemplateParameterList const* TPL = Described->getTemplateParameters(); + if(auto const* RC = TPL->getRequiresClause()) { RC = SubstituteConstraintExpressionWithoutSatisfaction( sema_, cast(isa(Described) ? Described : Templated), RC); @@ -453,7 +457,7 @@ generateUSR(const Decl* D) const if(auto* FD = dyn_cast(Templated); FD && FD->getTrailingRequiresClause()) { - const Expr* RC = FD->getTrailingRequiresClause(); + Expr const* RC = FD->getTrailingRequiresClause(); RC = SubstituteConstraintExpressionWithoutSatisfaction( sema_, cast(Described ? Described : Templated), RC); if (!RC) @@ -472,7 +476,7 @@ generateUSR(const Decl* D) const bool ASTVisitor:: generateID( - const Decl* D, + Decl const* D, SymbolID& id) const { if (!D) @@ -498,7 +502,7 @@ generateID( SymbolID ASTVisitor:: -generateID(const Decl* D) const +generateID(Decl const* D) const { SymbolID id = SymbolID::invalid; generateID(D, id); @@ -578,7 +582,7 @@ populate( { auto const existing = std::ranges:: find_if(I.Loc, - [line, file](const Location& l) + [line, file](Location const& l) { return l.LineNumber == line && l.FullPath == file->full_path; @@ -1556,6 +1560,201 @@ populateAttributes(InfoTy& I, Decl const* D) } } +void +ASTVisitor:: +addMember( + NamespaceInfo& I, + Info const& Member) +{ + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Namespaces, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.NamespaceAliases, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Typedefs, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Records, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Enums, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Functions, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Variables, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Concepts, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Guides, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Members.Usings, *U); + return; + } + report::error("Cannot push {} of type {} into members of namespace {}", + Member.Name, + mrdocs::toString(Member.Kind).c_str(), + I.Name); +} + +void +ASTVisitor:: +addMember( + RecordInfo& I, + Info const& Member) +{ + switch (Member.Access) + { + case AccessKind::Public: + addMember(I.Interface.Public, Member); + break; + case AccessKind::Private: + addMember(I.Interface.Private, Member); + break; + case AccessKind::Protected: + addMember(I.Interface.Protected, Member); + break; + default: + MRDOCS_UNREACHABLE(); + } +} + +void +ASTVisitor:: +addMember(RecordTranche& T, Info const& Member) +{ + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.NamespaceAliases, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Typedefs, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Records, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Enums, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + if (U->StorageClass != StorageClassKind::Static) + { + addMember(T.Functions, *U); + } + else + { + addMember(T.StaticFunctions, *U); + } + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Variables, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.StaticVariables, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Concepts, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Guides, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Friends, *U); + return; + } + if (auto const* U = dynamic_cast(&Member)) + { + addMember(T.Usings, *U); + return; + } + report::error("Cannot push {} of type {} into tranche", + Member.Name, + mrdocs::toString(Member.Kind).c_str()); +} + +void +ASTVisitor:: +addMember(EnumInfo& I, Info const& Member) const +{ + if (auto const* U = dynamic_cast(&Member)) + { + addMember(I.Constants, *U); + return; + } + report::error("Cannot push {} of type {} into members of enum {}", + Member.Name, + mrdocs::toString(Member.Kind).c_str(), + I.Name); +} + +void +ASTVisitor:: +addMember(OverloadsInfo& I, Info const& Member) const +{ + if (Member.isFunction()) + { + addMember(I.Members, Member); + return; + } + report::error("Cannot push {} of type {} into members of enum {}", + Member.Name, + mrdocs::toString(Member.Kind).c_str(), + I.Name); +} + +void +ASTVisitor:: +addMember(std::vector& container, Info const& Member) const +{ + if (std::ranges::find(container, Member.id) == container.end()) + { + container.push_back(Member.id); + } +} + template DeclTy> std::string ASTVisitor:: @@ -2860,7 +3059,7 @@ checkSymbolFiltersImpl( Info* ASTVisitor:: -find(SymbolID const& id) +find(SymbolID const& id) const { if (auto const it = info_.find(id); it != info_.end()) { @@ -2871,7 +3070,7 @@ find(SymbolID const& id) Info* ASTVisitor:: -find(Decl const* D) +find(Decl const* D) const { auto ID = generateID(D); MRDOCS_CHECK_OR(ID, nullptr); diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index f348e9e716..27a81ec909 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -388,13 +388,13 @@ class ASTVisitor std::derived_from DeclTy> requires (!std::derived_from) void - traverseParents(InfoTy& I, DeclTy const* DC); + traverseParent(InfoTy& I, DeclTy const* DC); template < std::derived_from InfoTy, std::derived_from DeclTy> void - traverseParents(InfoTy& I, DeclTy const* DC); + traverseParent(InfoTy& I, DeclTy const* DC); /* Generates a Unified Symbol Resolution value for a declaration. @@ -641,7 +641,7 @@ class ASTVisitor template InfoTy> static void - populateAttributes(InfoTy& I, const Decl* D); + populateAttributes(InfoTy& I, Decl const* D); // ================================================= // Populate function helpers @@ -664,6 +664,24 @@ class ASTVisitor SmallString<256> qualifiedName(NamedDecl const* ND) const; + void + addMember(NamespaceInfo& I, Info const& Member); + + void + addMember(RecordInfo& I, Info const& Member); + + void + addMember(RecordTranche& I, Info const& Member); + + void + addMember(EnumInfo& I, Info const& Member) const; + + void + addMember(OverloadsInfo& I, Info const& Member) const; + + void + addMember(std::vector& container, Info const& Member) const; + /* Parse the comments above a declaration as Javadoc This function will parse the comments above a declaration @@ -979,7 +997,7 @@ class ASTVisitor /* Get Info from ASTVisitor InfoSet */ Info* - find(SymbolID const& id); + find(SymbolID const& id) const; /* Get Info from ASTVisitor InfoSet @@ -988,7 +1006,7 @@ class ASTVisitor to get the Info object for the declaration. */ Info* - find(Decl const* D); + find(Decl const* D) const; /* Find or traverse a declaration diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp index e641971214..2e80a69f7a 100644 --- a/src/lib/AST/ParseJavadoc.cpp +++ b/src/lib/AST/ParseJavadoc.cpp @@ -1207,16 +1207,19 @@ visitBlockCommandComment( case CommandTraits::KCI_returns: case CommandTraits::KCI_result: { - auto itr = std::ranges::find_if( - jd_.getBlocks(), - [&](const PolymorphicValue & b) - { - return b->kind == doc::Kind::returns; - }); - if (itr != jd_.getBlocks().end()) - { - report::warn("{}: Duplicate @returns statement", C->getBeginLoc().printToString(sm_)); - } + // auto itr = std::ranges::find_if( + // jd_.getBlocks(), + // [&](const PolymorphicValue & b) + // { + // return b->kind == doc::Kind::returns; + // }); + // Duplicate @returns statement is not an error: + // https://github.com/microsoft/vscode-cpptools/issues/9316 + // MrDocs does not support duplicate @returns statements yet. + // if (itr != jd_.getBlocks().end()) + // { + // report::warn("{}: Duplicate @returns statement", C->getBeginLoc().printToString(sm_)); + // } doc::Returns returns; auto scope = enterScope(returns); diff --git a/src/lib/Dom/String.cpp b/src/lib/Dom/String.cpp index 6db130b6de..5726065792 100644 --- a/src/lib/Dom/String.cpp +++ b/src/lib/Dom/String.cpp @@ -21,7 +21,7 @@ class String::impl_view public: constexpr - impl_view(const char* ptr) + impl_view(char const* ptr) : impl_(const_cast(ptr)) { } @@ -61,7 +61,7 @@ impl() const noexcept void String:: construct( - const char* s, + char const* s, std::size_t n) { char* ptr = static_cast(::operator new( @@ -90,7 +90,7 @@ construct( } String:: -String(const String& other) noexcept +String(String const& other) noexcept : ptr_(other.ptr_) { if(! empty() && ! is_literal()) @@ -122,7 +122,7 @@ size() const noexcept return impl().size(); } -const char* +char const* String:: data() const noexcept { @@ -130,7 +130,7 @@ data() const noexcept // typically for the pointer is used // as a empty null terminated string if(empty()) - return reinterpret_cast(&ptr_); + return reinterpret_cast(&ptr_); if(is_literal()) return ptr_; return impl().data(); diff --git a/src/lib/Gen/hbs/Builder.cpp b/src/lib/Gen/hbs/Builder.cpp index 2966e7b4c8..eb6647d62e 100644 --- a/src/lib/Gen/hbs/Builder.cpp +++ b/src/lib/Gen/hbs/Builder.cpp @@ -312,36 +312,28 @@ createContext( return ctx; } -dom::Object -Builder:: -createContext( - OverloadSet const& I) -{ - dom::Object ctx; - ctx.set("symbol", domCorpus.construct(I)); - ctx.set("config", domCorpus->config.object()); - return ctx; -} - -template -requires std::derived_from || std::same_as +template T> Expected Builder:: operator()(std::ostream& os, T const& I) { std::string const templateFile = fmt::format("index.{}.hbs", domCorpus.fileExtension); - dom::Object ctx = createContext(I); + dom::Object const ctx = createContext(I); - auto& config = domCorpus->config; - bool isSinglePage = !config->multipage; - if (config->embedded || - isSinglePage) + if (auto& config = domCorpus->config; + config->embedded || + !config->multipage) { + // Single page or embedded pages render the index template directly + // without the wrapper return callTemplate(os, templateFile, ctx); } + // Multipage output: render the wrapper template + // The context receives the original symbol and the contents from rendering + // the index template auto const wrapperFile = fmt::format("wrapper.{}.hbs", domCorpus.fileExtension); - dom::Object wrapperCtx = createFrame(ctx); + dom::Object const wrapperCtx = createFrame(ctx); wrapperCtx.set("contents", dom::makeInvocable([this, &I, templateFile, &os]( dom::Value const&) -> Expected { @@ -352,6 +344,10 @@ operator()(std::ostream& os, T const& I) return callTemplate(os, wrapperFile, wrapperCtx); } +// Compile the Builder::operator() for each Info type +#define INFO(T) template Expected Builder::operator()(std::ostream&, T##Info const&); +#include + Expected Builder:: renderWrapped( @@ -437,11 +433,6 @@ commonTemplatesDir(std::string_view subdir) const subdir); } -// Define Builder::operator() for each Info type -#define INFO(T) template Expected Builder::operator()(std::ostream&, T##Info const&); -#include - -template Expected Builder::operator()(std::ostream&, OverloadSet const&); } // hbs } // mrdocs diff --git a/src/lib/Gen/hbs/Builder.hpp b/src/lib/Gen/hbs/Builder.hpp index b52de81888..e2cc3894fa 100644 --- a/src/lib/Gen/hbs/Builder.hpp +++ b/src/lib/Gen/hbs/Builder.hpp @@ -57,8 +57,7 @@ class Builder this function renders the wrapper template with the index template as the contents. */ - template - requires std::derived_from || std::same_as + template T> Expected operator()(std::ostream& os, T const&); @@ -115,10 +114,6 @@ class Builder dom::Object createContext(Info const& I); - /// @copydoc createContext(Info const&) - dom::Object - createContext(OverloadSet const& OS); - /** Render a Handlebars template from the templates directory. */ Expected diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.cpp b/src/lib/Gen/hbs/HandlebarsCorpus.cpp index 827e67af35..0f9617b8cb 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.cpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.cpp @@ -18,9 +18,7 @@ #include #include -namespace clang { -namespace mrdocs { -namespace hbs { +namespace clang::mrdocs::hbs { namespace { @@ -140,35 +138,15 @@ construct(Info const& I) const return obj; } -dom::Object -HandlebarsCorpus:: -construct( - OverloadSet const& I) const -{ - auto obj = this->DomCorpus::construct(I); - obj.set("url", getURL(I)); - obj.set("anchor", names_.getQualified(I, '-')); - return obj; -} - -template -requires std::derived_from || std::same_as std::string HandlebarsCorpus:: -getURL(T const& I) const +getURL(Info const& I) const { bool const multipage = getCorpus().config->multipage; char const prefix = multipage ? '/' : '#'; char const delim = multipage ? '/' : '-'; std::string href(1, prefix); - if constexpr (std::derived_from) - { - href += names_.getQualified(I.id, delim); - } - else if constexpr (std::same_as) - { - href += names_.getQualified(I, delim); - } + href += names_.getQualified(I.id, delim); if (multipage) { href.append("."); @@ -177,13 +155,6 @@ getURL(T const& I) const return href; } -// Define Builder::operator() for each Info type -#define INFO(T) template std::string HandlebarsCorpus::getURL(T## Info const&) const; -#include - -template std::string HandlebarsCorpus::getURL(OverloadSet const&) const; - - dom::Value HandlebarsCorpus:: getJavadoc(Javadoc const& jd) const @@ -261,6 +232,4 @@ getJavadoc(Javadoc const& jd) const return dom::Object(std::move(objKeyValues)); } -} // hbs -} // mrdocs -} // clang +} // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.hpp b/src/lib/Gen/hbs/HandlebarsCorpus.hpp index e4e39ddb57..3623811e87 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.hpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.hpp @@ -15,11 +15,10 @@ #include #include "lib/Support/LegibleNames.hpp" #include -#include +#include +#include -namespace clang { -namespace mrdocs { -namespace hbs { +namespace clang::mrdocs::hbs { /** A specialized DomCorpus for generating Handlebars values. @@ -28,7 +27,7 @@ namespace hbs { generation. */ -class HandlebarsCorpus : public DomCorpus +class HandlebarsCorpus final : public DomCorpus { public: /** Legible names for the Handlebars corpus. */ @@ -67,24 +66,13 @@ class HandlebarsCorpus : public DomCorpus dom::Object construct(Info const& I) const override; - /** Construct a dom::Object from the given OverloadSet. - - @param os The OverloadSet to get the overloads for. - @return A dom::Object representing the overloads. - */ - dom::Object - construct( - OverloadSet const& os) const override; - /** Get the cross-reference for the given Info. @param I The Info object to get the cross-reference for. @return A string representing the cross-reference. */ - template - requires std::derived_from || std::same_as std::string - getURL(T const& I) const; + getURL(Info const& I) const; /** Return a Dom value representing the Javadoc. @@ -95,8 +83,6 @@ class HandlebarsCorpus : public DomCorpus getJavadoc(Javadoc const& jd) const override; }; -} // hbs -} // mrdocs -} // clang +} // clang::mrdocs::hbs #endif diff --git a/src/lib/Gen/hbs/MultiPageVisitor.cpp b/src/lib/Gen/hbs/MultiPageVisitor.cpp index 8d17e73988..6681b41832 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.cpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.cpp @@ -16,42 +16,23 @@ namespace clang::mrdocs::hbs { -template -requires std::derived_from || std::same_as +template T> void MultiPageVisitor:: -operator()(T const& I0) +operator()(T const& I) { - if constexpr (std::derived_from) - { - MRDOCS_CHECK_OR(shouldGenerate(I0)); - } + MRDOCS_CHECK_OR(shouldGenerate(I)); // Increment the count count_.fetch_add(1, std::memory_order_relaxed); - // If T is an OverloadSet, we make a copy for the lambda because - // these are temporary objects that don't live in the corpus. - // Otherwise, the lambda will capture a reference to the corpus Info. - auto Ref = [&I0] { - if constexpr (std::derived_from) - { - return std::ref(I0); - } - else if constexpr (std::same_as) - { - return OverloadSet(I0); - } - }(); - ex_.async([this, Ref](Builder& builder) + ex_.async([this, &I](Builder& builder) { - T const& I = Ref; - // =================================== // Open the output file // =================================== - std::string path = files::appendPath(outputPath_, builder.domCorpus.getURL(I)); - std::string dir = files::getParentDir(path); + std::string const path = files::appendPath(outputPath_, builder.domCorpus.getURL(I)); + std::string const dir = files::getParentDir(path); if (auto exp = files::createDirectory(dir); !exp) { exp.error().Throw(); @@ -86,26 +67,11 @@ operator()(T const& I0) // =================================== // Traverse the symbol members // =================================== - if constexpr (std::derived_from) - { - if constexpr( - T::isNamespace() || - T::isRecord() || - T::isEnum()) - { - corpus_.traverseOverloads(I, *this); - } - } - else if constexpr (std::same_as) - { - corpus_.traverse(I, *this); - } + corpus_.traverse(I, *this); }); } #define INFO(T) template void MultiPageVisitor::operator()(T##Info const&); #include -template void MultiPageVisitor::operator()(OverloadSet const&); - } // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/MultiPageVisitor.hpp b/src/lib/Gen/hbs/MultiPageVisitor.hpp index c88566e835..d2dfd7df2b 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.hpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.hpp @@ -49,8 +49,7 @@ class MultiPageVisitor respective tasks are also pushed to the executor group. */ - template - requires std::derived_from || std::same_as + template T> void operator()(T const& I); diff --git a/src/lib/Gen/hbs/SinglePageVisitor.cpp b/src/lib/Gen/hbs/SinglePageVisitor.cpp index 4b32246d7b..5342301896 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.cpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.cpp @@ -16,36 +16,16 @@ namespace clang::mrdocs::hbs { -template -requires std::derived_from || std::same_as +template T> void SinglePageVisitor:: -operator()(T const& I0) +operator()(T const& I) { - if constexpr (std::derived_from) + MRDOCS_CHECK_OR(shouldGenerate(I)); + ex_.async([this, &I, symbolIdx = numSymbols_++](Builder& builder) { - MRDOCS_CHECK_OR(shouldGenerate(I0)); - } - - // If T is an OverloadSet, we make a copy for the lambda because - // these are temporary objects that don't live in the corpus. - // Otherwise, the lambda will capture a reference to the corpus Info. - auto Ref = [&I0] { - if constexpr (std::derived_from) - { - return std::ref(I0); - } - else if constexpr (std::same_as) - { - return OverloadSet(I0); - } - }(); - ex_.async([this, Ref, symbolIdx = numSymbols_++](Builder& builder) - { - T const& I = Ref; - - // Output to an independent string first, then write to - // the shared stream + // Output to an independent string first (async), then write to + // the shared stream (sync) std::stringstream ss; if(auto r = builder(ss, I)) { @@ -56,24 +36,15 @@ operator()(T const& I0) r.error().Throw(); } }); - - if constexpr (std::derived_from) - { - if constexpr( - T::isNamespace() || - T::isRecord() || - T::isEnum()) - { - corpus_.orderedTraverseOverloads(I0, *this); - } - } - else if constexpr (std::same_as) - { - corpus_.orderedTraverse(I0, *this); - } - + Corpus::TraverseOptions opts = { + .ordered = true, + .skipInherited = std::same_as}; + corpus_.traverse(opts, I, *this); } +#define INFO(T) template void SinglePageVisitor::operator()(T##Info const&); +#include + // pageNumber is zero-based void SinglePageVisitor:: @@ -126,9 +97,4 @@ writePage( } } -#define INFO(T) template void SinglePageVisitor::operator()(T##Info const&); -#include - -template void SinglePageVisitor::operator()(OverloadSet const&); - } // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/SinglePageVisitor.hpp b/src/lib/Gen/hbs/SinglePageVisitor.hpp index b408f844b1..c892e0e645 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.hpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.hpp @@ -54,8 +54,7 @@ class SinglePageVisitor respective tasks are also pushed to the executor group. */ - template - requires std::derived_from || std::same_as + template T> void operator()(T const& I); }; diff --git a/src/lib/Gen/hbs/VisitorHelpers.cpp b/src/lib/Gen/hbs/VisitorHelpers.cpp index cbf5a6382e..03d2029283 100644 --- a/src/lib/Gen/hbs/VisitorHelpers.cpp +++ b/src/lib/Gen/hbs/VisitorHelpers.cpp @@ -71,35 +71,48 @@ 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) + MRDOCS_CHECK_OR(parent, nullptr); + return visit(parent, [&](InfoTy const& U) + -> Info const* { - Info const* sibling = c.find(siblingID); - if (!sibling || - !shouldGenerate(*sibling) || - sibling->Name != I.Name) + if constexpr (InfoParent) { - continue; - } - bool const isPrimarySibling = visit(*sibling, [&](auto const& U) + namespace stdv = std::ranges::views; + auto sameNameSiblings = + allMembers(U) | + stdv::transform([&](SymbolID const& siblingID) + { + return c.find(siblingID); + }) | + stdv::filter([&](Info const* sibling) + { + return sibling && sibling->Name == I.Name; + }); + for (Info const* sibling: sameNameSiblings) { - if constexpr (requires { U.Template; }) + if (!sibling || + !shouldGenerate(*sibling)) { - std::optional const& Template = U.Template; - MRDOCS_CHECK_OR(Template, false); - return !Template->Params.empty() && Template->Args.empty(); + continue; } - return false; - }); - if (!isPrimarySibling) - { - continue; + bool const isPrimarySibling = visit(*sibling, [&](auto const& V) + { + if constexpr (requires { V.Template; }) + { + std::optional const& Template = V.Template; + MRDOCS_CHECK_OR(Template, false); + return !Template->Params.empty() && Template->Args.empty(); + } + return false; + }); + if (isPrimarySibling) + { + return sibling; + } + } } - return sibling; - } - return nullptr; + return nullptr; + }); } Info const* diff --git a/src/lib/Gen/xml/CXXTags.hpp b/src/lib/Gen/xml/CXXTags.hpp index 0ff28d42ad..6377394a12 100644 --- a/src/lib/Gen/xml/CXXTags.hpp +++ b/src/lib/Gen/xml/CXXTags.hpp @@ -120,7 +120,7 @@ writeAttr( inline void -writeTemplateArg(const TArg& I, XMLTags& tags); +writeTemplateArg(TArg const& I, XMLTags& tags); inline void @@ -130,7 +130,7 @@ writeType( std::string_view type_tag = "type") { visit(I, [&]( - const T& t) + T const& t) { Attributes attrs = { { "class", toString(T::kind_id), @@ -218,7 +218,7 @@ writeType( if constexpr(T::isFunction()) { writeType(*t.ReturnType, tags, "return-type"); - for(const auto& p : t.ParamTypes) + for(auto const& p : t.ParamTypes) writeType(*p, tags, "param-type"); } @@ -257,9 +257,9 @@ inline void writeParam(Param const& P, XMLTags& tags) tags.close(paramTagName); } -inline void writeTemplateParam(const TParam& I, XMLTags& tags) +inline void writeTemplateParam(TParam const& I, XMLTags& tags) { - visit(I, [&](const T& P) + visit(I, [&](T const& P) { Attributes attrs = { {"name", P.Name, ! P.Name.empty()}, diff --git a/src/lib/Gen/xml/XMLTags.cpp b/src/lib/Gen/xml/XMLTags.cpp index 04f90394c7..2885bdf12c 100644 --- a/src/lib/Gen/xml/XMLTags.cpp +++ b/src/lib/Gen/xml/XMLTags.cpp @@ -72,7 +72,7 @@ write( //------------------------------------------------ std::string -toString( +toBase64Str( SymbolID const& id) { return toBase64(id); @@ -103,7 +103,7 @@ Attributes( void Attributes:: -push(const Attribute& attr) +push(Attribute const& attr) { attrs_.push_back(attr); } diff --git a/src/lib/Gen/xml/XMLTags.hpp b/src/lib/Gen/xml/XMLTags.hpp index 592a0c2220..b797e77442 100644 --- a/src/lib/Gen/xml/XMLTags.hpp +++ b/src/lib/Gen/xml/XMLTags.hpp @@ -61,7 +61,7 @@ struct xmlEscape //------------------------------------------------ // Converters for attributes -std::string toString(SymbolID const& id); +std::string toBase64Str(SymbolID const& id); //------------------------------------------------ @@ -86,7 +86,7 @@ struct Attribute Attribute( SymbolID id) : name("id") - , value(toString(id)) + , value(toBase64Str(id)) , pred(id != SymbolID::invalid) { } diff --git a/src/lib/Gen/xml/XMLWriter.cpp b/src/lib/Gen/xml/XMLWriter.cpp index b23298b94d..b2df4aa297 100644 --- a/src/lib/Gen/xml/XMLWriter.cpp +++ b/src/lib/Gen/xml/XMLWriter.cpp @@ -26,9 +26,7 @@ // //------------------------------------------------ -namespace clang { -namespace mrdocs { -namespace xml { +namespace clang::mrdocs::xml { struct XMLWriter::XmlKey { @@ -54,9 +52,7 @@ struct XMLWriter::GenKey } }; -} // xml -} // mrdocs -} // clang +} // clang::mrdocs::xml template<> struct llvm::yaml::MappingTraits< @@ -97,9 +93,7 @@ struct llvm::yaml::MappingTraits< } }; -namespace clang { -namespace mrdocs { -namespace xml { +namespace clang::mrdocs::xml { //------------------------------------------------ // @@ -195,13 +189,11 @@ XMLWriter:: operator()( T const& I) { - Info const& base = I; - if (base.Extraction == ExtractionMode::Dependency) + if (Info const& base = I; + base.Extraction == ExtractionMode::Dependency) { return; } - - #define INFO(Type) if constexpr(T::is##Type()) write##Type(I); #include } @@ -220,9 +212,11 @@ writeNamespace( { "is-inline", "1", I.IsInline} }); writeJavadoc(I.javadoc); - for(const SymbolID& id : I.UsingDirectives) + for (SymbolID const& id: I.UsingDirectives) + { tags_.write("using-directive", {}, { { id } }); - corpus_.traverse(I, *this); + } + corpus_.traverse({.ordered=true}, I, *this); tags_.close(namespaceTagName); } @@ -248,7 +242,7 @@ writeEnum( writeJavadoc(I.javadoc); - corpus_.traverse(I, *this); + corpus_.traverse({.ordered=true}, I, *this); tags_.close(enumTagName); } @@ -358,6 +352,14 @@ writeFunction( closeTemplate(I.Template); } +void +XMLWriter:: +writeOverloads( + OverloadsInfo const& I) +{ + corpus_.traverse({.ordered=true}, I, *this); +} + void XMLWriter:: writeGuide( @@ -511,7 +513,7 @@ writeRecord( writeJavadoc(I.javadoc); - corpus_.traverse(I, *this); + corpus_.traverse({.ordered=true}, I, *this); tags_.close(tagName); @@ -550,7 +552,7 @@ writeTypedef( void XMLWriter:: writeField( - const FieldInfo& I) + FieldInfo const& I) { std::string_view tag_name = dataMemberTagName; std::string bit_width; @@ -668,9 +670,9 @@ openTemplate( {I->Primary} }); - for(const auto& tparam : I->Params) + for(auto const& tparam : I->Params) writeTemplateParam(*tparam, tags_); - for(const auto& targ : I->Args) + for(auto const& targ : I->Args) writeTemplateArg(*targ, tags_); } @@ -687,17 +689,17 @@ closeTemplate( void XMLWriter:: writeSpecialization( - const SpecializationInfo& I) + SpecializationInfo const& I) { tags_.open(specializationTagName, { {I.id}, - {"primary", toString(I.Primary) } + {"primary", toBase64Str(I.Primary) } }); - for(const auto& targ : I.Args) + for(auto const& targ : I.Args) writeTemplateArg(*targ, tags_); - corpus_.traverse(I, *this); + corpus_.traverse({.ordered=true}, I, *this); tags_.close(specializationTagName); } @@ -1060,6 +1062,4 @@ writeTParam( tags_.close("tparam"); } -} // xml -} // mrdocs -} // clang +} // clang::mrdocs::xml diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 52135a3093..e6329ad841 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -171,6 +171,26 @@ "type": "bool", "default": true }, + { + "name": "overloads", + "brief": "Detect and group function overloads", + "details": "When set to `true`, MrDocs detects function overloads and groups them as a single symbol type.", + "type": "bool", + "default": true + }, + { + "name": "inherit-base-members", + "brief": "Determine how derived classes inherit base members", + "details": "Determine how derived classes inherit members of base classes. When set to `never`, derived classes do not inherit members of base classes and only the relationship is stored. When set to `reference`, derived classes list members of base classes but references are still linked to the base class. When set to `copy`, a copy is created for each base symbol as if it was declared in the derived class. If the base class is a dependency, the extraction mode is copied from the new parent. When set to `copy-dependencies`, a reference is created by default and a copy is created when the base class is a dependency.", + "type": "enum", + "values": [ + "never", + "reference", + "copy", + "copy-dependencies" + ], + "default": "copy-dependencies" + }, { "name": "private-members", "brief": "Extraction policy for private class members", diff --git a/src/lib/Lib/Corpus.cpp b/src/lib/Lib/Corpus.cpp index 46f985be2b..56859b8425 100644 --- a/src/lib/Lib/Corpus.cpp +++ b/src/lib/Lib/Corpus.cpp @@ -15,8 +15,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { //------------------------------------------------ @@ -54,7 +53,7 @@ globalNamespace() const noexcept void Corpus:: qualifiedName( - const Info& I, + Info const& I, std::string& temp) const { temp.clear(); @@ -104,7 +103,7 @@ qualifiedName( } std::vector -getParents(Corpus const& C, const Info& I) +getParents(Corpus const& C, Info const& I) { std::vector parents; std::size_t n = 0; @@ -127,5 +126,4 @@ getParents(Corpus const& C, const Info& I) return parents; } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index 375a7d06df..7f8e0d3e34 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -22,8 +22,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { auto CorpusImpl:: @@ -35,12 +34,12 @@ begin() const noexcept -> // KRYSTIAN NOTE: this is far from ideal, but i'm not sure // to what extent implementation detail should be hidden. return iterator(this, info_.begin()->get(), - [](const Corpus* corpus, const Info* val) -> - const Info* + [](Corpus const* corpus, Info const* val) -> + Info const* { MRDOCS_ASSERT(val); - const CorpusImpl* impl = - static_cast(corpus); + CorpusImpl const* impl = + static_cast(corpus); auto it = impl->info_.find(val->id); if(++it == impl->info_.end()) return nullptr; @@ -93,7 +92,7 @@ build( // Create empty corpus // ------------------------------------------ // The corpus will keep a reference to Config. - std::unique_ptr corpus = std::make_unique(config); + auto corpus = std::make_unique(config); // ------------------------------------------ // Execution context @@ -191,10 +190,8 @@ build( "Warning: mapping failed because ", err); } - auto results = context.results(); - if(! results) - return Unexpected(results.error()); - corpus->info_ = std::move(results.value()); + MRDOCS_TRY(auto results, context.results()); + corpus->info_ = std::move(results); report::info( "Extracted {} declarations in {}", @@ -204,11 +201,9 @@ build( // ------------------------------------------ // Finalize corpus // ------------------------------------------ - auto lookup = std::make_unique(*corpus); - finalize(corpus->info_, *lookup); + finalize(*corpus); return corpus; } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Lib/CorpusImpl.hpp b/src/lib/Lib/CorpusImpl.hpp index 96369ff2dc..43e26c6fe9 100644 --- a/src/lib/Lib/CorpusImpl.hpp +++ b/src/lib/Lib/CorpusImpl.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -22,8 +23,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** Implements the Corpus. @@ -38,6 +38,9 @@ namespace mrdocs { */ class CorpusImpl : public Corpus { + friend + void + finalize(CorpusImpl& corpus); public: /** Constructor. */ @@ -127,7 +130,6 @@ get( return *t; } -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/src/lib/Lib/ExecutionContext.cpp b/src/lib/Lib/ExecutionContext.cpp index ad56642cae..43d24df90c 100644 --- a/src/lib/Lib/ExecutionContext.cpp +++ b/src/lib/Lib/ExecutionContext.cpp @@ -45,26 +45,6 @@ mergeInfos(std::vector>& Values) } #endif -/** Merges two Info objects. - - This function is used to merge two Info objects with the same SymbolID. - The function assumes that the two Info objects are of the same type. - If they are not, the function will fail. - - @param I The Info object to merge into. - @param Other The Info object to merge from. -*/ -void -merge(Info& I, Info&& Other) -{ - MRDOCS_ASSERT(I.Kind == Other.Kind); - MRDOCS_ASSERT(I.id == Other.id); - visit(I, [&](InfoTy& II) mutable - { - merge(II, static_cast(Other)); - }); -} - } // (anon) // ---------------------------------------------------------------- diff --git a/src/lib/Lib/ExecutionContext.hpp b/src/lib/Lib/ExecutionContext.hpp index 565316ec4d..3d67a8cc47 100644 --- a/src/lib/Lib/ExecutionContext.hpp +++ b/src/lib/Lib/ExecutionContext.hpp @@ -44,7 +44,7 @@ namespace mrdocs { class ExecutionContext { protected: - const ConfigImpl& config_; + ConfigImpl const& config_; public: virtual ~ExecutionContext() = default; @@ -57,7 +57,7 @@ class ExecutionContext @param config The configuration to use. */ ExecutionContext( - const ConfigImpl& config) + ConfigImpl const& config) : config_(config) { } diff --git a/src/lib/Lib/Info.cpp b/src/lib/Lib/Info.cpp index 82dc397f56..17ceabdbf8 100644 --- a/src/lib/Lib/Info.cpp +++ b/src/lib/Lib/Info.cpp @@ -17,7 +17,7 @@ operator()( std::size_t InfoPtrHasher:: operator()( - const SymbolID& id) const + SymbolID const& id) const { return std::hash()(id); } @@ -40,7 +40,7 @@ bool InfoPtrEqual:: operator()( const std::unique_ptr& a, - const SymbolID& b) const + SymbolID const& b) const { MRDOCS_ASSERT(a); return a->id == b; @@ -49,7 +49,7 @@ operator()( bool InfoPtrEqual:: operator()( - const SymbolID& a, + SymbolID const& a, const std::unique_ptr& b) const { MRDOCS_ASSERT(b); diff --git a/src/lib/Lib/Info.hpp b/src/lib/Lib/Info.hpp index d1958c1dce..17fec6b723 100644 --- a/src/lib/Lib/Info.hpp +++ b/src/lib/Lib/Info.hpp @@ -18,8 +18,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** A hash function for Info pointers. @@ -51,7 +50,7 @@ struct InfoPtrHasher */ std::size_t operator()( - const SymbolID& id) const; + SymbolID const& id) const; }; /** Equality comparison for Info pointers. @@ -89,7 +88,7 @@ struct InfoPtrEqual bool operator()( const std::unique_ptr& a, - const SymbolID& b) const; + SymbolID const& b) const; /** Returns `true` if the SymbolID is equal to the id of the Info object. @@ -103,7 +102,7 @@ struct InfoPtrEqual */ bool operator()( - const SymbolID& a, + SymbolID const& a, const std::unique_ptr& b) const; }; @@ -117,7 +116,6 @@ struct InfoPtrEqual using InfoSet = std::unordered_set< std::unique_ptr, InfoPtrHasher, InfoPtrEqual>; -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/src/lib/Lib/Lookup.cpp b/src/lib/Lib/Lookup.cpp index 31e00b2f91..949c643e8b 100644 --- a/src/lib/Lib/Lookup.cpp +++ b/src/lib/Lib/Lookup.cpp @@ -11,25 +11,26 @@ #include "Lookup.hpp" #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { namespace { -bool supportsLookup(const Info* info) +bool +supportsLookup(Info const* I) { - return info && - (info->isRecord() || - info->isNamespace() || - info->isEnum() || - info->isSpecialization()); + MRDOCS_CHECK_OR(I, false); + return visit(*I, []( + InfoTy const&) -> bool + { + return InfoParent; + }); } -bool isTransparent(const Info* info) +bool isTransparent(Info const* info) { MRDOCS_ASSERT(info); return visit(*info, []( - const InfoTy& I) -> bool + InfoTy const& I) -> bool { if constexpr(InfoTy::isNamespace()) return I.IsInline; @@ -41,24 +42,23 @@ bool isTransparent(const Info* info) void buildLookups( - const Corpus& corpus, - const Info& info, + Corpus const& corpus, + Info const& info, LookupTable& lookups) { - visit(info, [&](const InfoTy& I) + visit(info, [&](InfoTy const& I) { - if constexpr( - InfoTy::isRecord() || - InfoTy::isNamespace() || - InfoTy::isEnum()) + if constexpr(InfoParent) { - for(const SymbolID& M : I.Members) + for (SymbolID const& M : allMembers(I)) { - const Info* child = corpus.find(M); + Info const* child = corpus.find(M); // if the member is an inline namespace or // an unscoped enumeration, add its members as well - if(isTransparent(child)) + if (isTransparent(child)) + { buildLookups(corpus, *child, lookups); + } // KRYSTIAN TODO: handle inline/anonymous namespaces // KRYSTIAN TODO: injected class names? @@ -74,27 +74,28 @@ buildLookups( LookupTable:: LookupTable( - const Info& info, - const Corpus& corpus) + Info const& info, + Corpus const& corpus) { MRDOCS_ASSERT(supportsLookup(&info)); - buildLookups(corpus, info, *this); } SymbolLookup:: -SymbolLookup(const Corpus& corpus) +SymbolLookup(Corpus const& corpus) : corpus_(corpus) { - for(const Info& I : corpus_) + for(Info const& I : corpus_) { - if(! supportsLookup(&I)) + if (!supportsLookup(&I)) + { continue; + } lookup_tables_.emplace(&I, LookupTable(I, corpus_)); } } -const Info* +Info const* SymbolLookup:: adjustLookupContext( Info const* context) @@ -108,33 +109,42 @@ adjustLookupContext( return context; } -const Info* +Info const* SymbolLookup:: -lookThroughTypedefs(const Info* I) +lookThroughTypedefs(Info const* I) { - if(! I || ! I->isTypedef()) + if (!I || !I->isTypedef()) + { return I; - auto* TI = static_cast(I); + } + auto* TI = static_cast(I); return lookThroughTypedefs( corpus_.find(TI->Type->namedSymbol())); } -const Info* +Info const* SymbolLookup:: lookupInContext( - const Info* context, - std::string_view name, - bool for_nns, + Info const* context, + std::string_view const name, + bool const for_nns, LookupCallback& callback) { // if the lookup context is a typedef, we want to // lookup the name in the type it denotes - if(! (context = lookThroughTypedefs(context))) + if (!((context = lookThroughTypedefs(context)))) + { return nullptr; + } MRDOCS_ASSERT(supportsLookup(context)); - LookupTable& table = lookup_tables_.at(context); // KRYSTIAN FIXME: disambiguation based on signature - for(auto& result : table.lookup(name)) + auto const it = lookup_tables_.find(context); + if (it == lookup_tables_.end()) + { + return nullptr; + } + for (LookupTable const& table = it->second; + auto& result : table.lookup(name)) { if(for_nns) { @@ -144,7 +154,7 @@ lookupInContext( // - types, and // - templates whose specializations are types // KRYSTIAN FIXME: should we if the result is acceptable? - if(result->isNamespace() || + if (result->isNamespace() || result->isRecord() || result->isEnum() || result->isTypedef()) @@ -154,8 +164,10 @@ lookupInContext( { // if we are looking up a terminal name, call the handler // to determine whether the result is acceptable - if(callback(*result)) + if (callback(*result)) + { return result; + } } } @@ -163,27 +175,30 @@ lookupInContext( // search base classes for the name if(context->isRecord()) { - const auto* RI = static_cast< - const RecordInfo*>(context); + auto const* RI = static_cast(context); // KRYSTIAN FIXME: resolve ambiguities & report errors - for(const auto& B : RI->Bases) + for(auto const& B : RI->Bases) { - if(const Info* result = lookupInContext( - corpus_.find(B.Type->namedSymbol()), - name, for_nns, callback)) + if (Info const* result = lookupInContext( + corpus_.find(B.Type->namedSymbol()), + name, + for_nns, + callback)) + { return result; + } } } return nullptr; } -const Info* +Info const* SymbolLookup:: lookupUnqualifiedImpl( - const Info* context, - std::string_view name, - bool for_nns, + Info const* context, + std::string_view const name, + bool const for_nns, LookupCallback& callback) { if (!context) @@ -206,34 +221,40 @@ lookupUnqualifiedImpl( MRDOCS_UNREACHABLE(); } -const Info* +Info const* SymbolLookup:: lookupQualifiedImpl( - const Info* context, + Info const* context, std::span qualifier, - std::string_view terminal, + std::string_view const terminal, LookupCallback& callback) { - if(! context) + if (!context) + { return nullptr; - if(qualifier.empty()) - return lookupInContext( - context, terminal, false, callback); + } + if (qualifier.empty()) + { + return lookupInContext(context, terminal, false, callback); + } context = lookupUnqualifiedImpl( context, qualifier.front(), true, callback); qualifier = qualifier.subspan(1); - if(! context) + if (!context) + { return nullptr; + } while(! qualifier.empty()) { - if(! (context = lookupInContext( - context, qualifier.front(), true, callback))) + if (!((context + = lookupInContext(context, qualifier.front(), true, callback)))) + { return nullptr; + } qualifier = qualifier.subspan(1); } return lookupInContext( context, terminal, false, callback); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Lib/Lookup.hpp b/src/lib/Lib/Lookup.hpp index aa430bc398..5200efaa5e 100644 --- a/src/lib/Lib/Lookup.hpp +++ b/src/lib/Lib/Lookup.hpp @@ -21,8 +21,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { class LookupTable { @@ -31,12 +30,12 @@ class LookupTable // (e.g. unscoped enums and inline namespaces) will // have their members added to the table as well std::unordered_multimap< - std::string_view, const Info*> lookups_; + std::string_view, Info const*> lookups_; public: LookupTable( - const Info& info, - const Corpus& corpus); + Info const& info, + Corpus const& corpus); auto lookup(std::string_view name) const { @@ -45,48 +44,51 @@ class LookupTable first, last) | std::views::values; } - void add(std::string_view name, const Info* info) + void add(std::string_view name, Info const* info) { lookups_.emplace(name, info); } }; +/* A tool for looking up symbols by name + + This class provides a way to look up symbols by name. + + It is mainly used to resolve references in the + documentation. + */ class SymbolLookup { - const Corpus& corpus_; + Corpus const& corpus_; // maps symbol ID to its lookup table, if lookup is supported std::unordered_map< - const Info*, + Info const*, LookupTable> lookup_tables_; struct LookupCallback { virtual ~LookupCallback() = default; - virtual bool operator()(const Info&) = 0; + virtual bool operator()(Info const&) = 0; }; template auto makeHandler(Fn& fn); - const Info* - adjustLookupContext(const Info* context); + Info const* + adjustLookupContext(Info const* context); - const Info* - lookThroughTypedefs(const Info* I); + Info const* + lookThroughTypedefs(Info const* I); - const Info* - getTypeAsTag( - const std::unique_ptr& T); - - const Info* + Info const* lookupInContext( - const Info* context, + Info const* context, std::string_view name, bool for_nns, LookupCallback& callback); - const Info* + Info const* lookupUnqualifiedImpl( const Info* context, std::string_view name, @@ -155,7 +157,6 @@ makeHandler(Fn& fn) return LookupCallbackImpl(fn); } -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/src/lib/Lib/MrDocsCompilationDatabase.cpp b/src/lib/Lib/MrDocsCompilationDatabase.cpp index 6e7161a45e..7e27b820a6 100644 --- a/src/lib/Lib/MrDocsCompilationDatabase.cpp +++ b/src/lib/Lib/MrDocsCompilationDatabase.cpp @@ -409,7 +409,7 @@ adjustCommandLine( // Add additional defines // ------------------------------------------------------ // These are additional defines specified in the config file - for(const auto& def : (*config)->defines) + for(auto const& def : (*config)->defines) { new_cmdline.emplace_back(fmt::format("-D{}", def)); } diff --git a/src/lib/Lib/TagfileWriter.cpp b/src/lib/Lib/TagfileWriter.cpp index bb89685cff..03799ac1ce 100644 --- a/src/lib/Lib/TagfileWriter.cpp +++ b/src/lib/Lib/TagfileWriter.cpp @@ -9,7 +9,7 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "../Gen/xml/CXXTags.hpp" +#include "lib/Gen/xml/CXXTags.hpp" #include "TagfileWriter.hpp" #include "lib/Gen/hbs/VisitorHelpers.hpp" #include "lib/Lib/ConfigImpl.hpp" @@ -137,7 +137,7 @@ writeNamespace( tags_.write("filename", generateFilename(I)); // Write the class-like members of this namespace - corpus_->orderedTraverse(I, [this](U const& J) + corpus_->traverse(I, [this](U const& J) { if (!hbs::shouldGenerate(J)) { @@ -154,7 +154,7 @@ writeNamespace( }); // Write the function-like members of this namespace - corpus_->orderedTraverse(I, [this](U const& J) + corpus_->traverse(I, [this](U const& J) { if constexpr (U::isFunction()) { @@ -166,7 +166,7 @@ writeNamespace( } // Write compound elements for the members of this namespace - corpus_->orderedTraverse(I, [this](U const& J) + corpus_->traverse(I, [this](U const& J) { this->operator()(J); }); @@ -187,7 +187,7 @@ writeClassLike( if constexpr (T::isRecord()) { // Write the function-like members of this record - corpus_->orderedTraverse(I, [this](U const& J) + corpus_->traverse(I, [this](U const& J) { if constexpr (U::isFunction()) { diff --git a/src/lib/Metadata/DomCorpus.cpp b/src/lib/Metadata/DomCorpus.cpp index ad80ccd716..1e129079d6 100644 --- a/src/lib/Metadata/DomCorpus.cpp +++ b/src/lib/Metadata/DomCorpus.cpp @@ -10,18 +10,18 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Support/Radix.hpp" +#include +#include #include "lib/Support/LegibleNames.hpp" -#include "lib/Dom/LazyObject.hpp" -#include "lib/Dom/LazyArray.hpp" -#include -#include +#include "lib/Support/Radix.hpp" #include #include #include +#include +#include +#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { class DomCorpus::Impl { @@ -29,8 +29,6 @@ class DomCorpus::Impl DomCorpus const& domCorpus_; Corpus const& corpus_; - std::unordered_map cache_; - std::mutex mutex_; public: Impl( @@ -48,34 +46,19 @@ class DomCorpus::Impl } dom::Object - create(Info const& I) + create(Info const& I) const { return domCorpus_.construct(I); } dom::Object - get(SymbolID const& id) + get(SymbolID const& id) const { // VFALCO Hack to deal with symbol IDs // being emitted without the corresponding data. const Info* I = corpus_.find(id); - if(! I) - return {}; // VFALCO Hack - - std::lock_guard lock(mutex_); - auto it = cache_.find(id); - if(it == cache_.end()) - { - auto obj = create(*I); - cache_.insert( - { id, obj.impl() }); - return obj; - } - if(auto sp = it->second.lock()) - return dom::Object(sp); - auto obj = create(*I); - it->second = obj.impl(); - return obj; + MRDOCS_CHECK_OR(I, {}); + return create(*I); } }; @@ -101,15 +84,10 @@ dom::Object DomCorpus:: construct(Info const& I) const { - return dom::ValueFrom(I, this).getObject(); -} - -dom::Object -DomCorpus:: -construct( - OverloadSet const& overloads) const -{ - return dom::ValueFrom(overloads, this).getObject(); + return visit(I, [this](T const& U) -> dom::Object + { + return dom::ValueFrom(U, this).getObject(); + }); } dom::Value @@ -131,5 +109,20 @@ getJavadoc(Javadoc const&) const return nullptr; } -} // mrdocs -} // clang +dom::Array +getParents(DomCorpus const& C, Info const& I) +{ + // A convenient list to iterate over the parents + // with resorting to partial template recursion + Corpus const& corpus = C.getCorpus(); + auto const pIds = getParents(corpus, I); + dom::Array res; + for (SymbolID const& id : pIds) + { + Info const& PI = corpus.get(id); + res.push_back(C.construct(PI)); + } + return res; +} + +} // clang::mrdocs diff --git a/src/lib/Metadata/Expression.cpp b/src/lib/Metadata/Expression.cpp new file mode 100644 index 0000000000..223c3c78cc --- /dev/null +++ b/src/lib/Metadata/Expression.cpp @@ -0,0 +1,25 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include + +namespace clang::mrdocs { + +void +merge(ExprInfo& I, ExprInfo&& Other) +{ + if (I.Written.empty()) + { + I.Written = std::move(Other.Written); + } +} + +} // clang::mrdocs diff --git a/src/lib/Metadata/Finalize.cpp b/src/lib/Metadata/Finalize.cpp index 2ea5e42833..814e7f41f7 100644 --- a/src/lib/Metadata/Finalize.cpp +++ b/src/lib/Metadata/Finalize.cpp @@ -9,476 +9,46 @@ // #include "Finalize.hpp" +#include "lib/Metadata/Finalizers/BaseMembersFinalizer.hpp" +#include "lib/Metadata/Finalizers/OverloadsFinalizer.hpp" +#include "lib/Metadata/Finalizers/ReferenceFinalizer.hpp" #include "lib/Lib/Info.hpp" -#include "lib/Support/NameParser.hpp" #include -#include -#include -#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { -/** Finalizes a set of Info. - - This removes any references to SymbolIDs - which do not exist. - - References which should always be valid are not checked. -*/ -class Finalizer +namespace { +void +finalizeBaseMembers(InfoSet& Info, Config const& config) { - InfoSet& info_; - SymbolLookup& lookup_; - Info* current_ = nullptr; - - bool resolveReference(doc::Reference& ref) - { - auto parse_result = parseIdExpression(ref.string); - if(! parse_result) - return false; - - if(parse_result->name.empty()) - return false; - - auto is_acceptable = [&](const Info& I) -> bool - { - // if we are copying the documentation of the - // referenced symbol, ignore the current declaration - if(ref.kind == doc::Kind::copied) - return &I != current_; - // otherwise, consider the result to be acceptable - return true; - }; - - const Info* found = nullptr; - if(parse_result->qualified) - { - Info* context = current_; - std::vector qualifier; - // KRYSTIAN FIXME: lookupQualified should accept - // std::vector as the qualifier - for(auto& part : parse_result->qualifier) - qualifier.push_back(part); - if(parse_result->qualifier.empty()) - { - MRDOCS_ASSERT(info_.contains(SymbolID::global)); - context = info_.find(SymbolID::global)->get(); - } - found = lookup_.lookupQualified( - context, - qualifier, - parse_result->name, - is_acceptable); - } - else - { - found = lookup_.lookupUnqualified( - current_, - parse_result->name, - is_acceptable); - } - - // prevent recursive documentation copies - if(ref.kind == doc::Kind::copied && - found && found->id == current_->id) - return false; - - // if we found a symbol, replace the reference - // ID with the SymbolID of that symbol - if(found) - ref.id = found->id; - return found; - } - - void - finalize(SymbolID& id) - { - if(id && ! info_.contains(id)) - id = SymbolID::invalid; - } - - void - finalize(std::vector& ids) - { - std::erase_if(ids, [this](const SymbolID& id) - { - return ! id || ! info_.contains(id); - }); - } - - void - finalize(TArg& arg) - { - visit(arg, [this](Ty& A) - { - if constexpr (Ty::isType()) - { - finalize(A.Type); - } - if constexpr (Ty::isTemplate()) - { - finalize(A.Template); - } - }); - } - - void finalize(TParam& param) - { - finalize(param.Default); - - visit(param, [this](Ty& P) - { - if constexpr(Ty::isType()) - finalize(P.Constraint); - - if constexpr(Ty::isNonType()) - finalize(P.Type); - - if constexpr(Ty::isTemplate()) - finalize(P.Params); - }); - } - - void finalize(Param& param) - { - finalize(param.Type); - } - - void finalize(BaseInfo& info) - { - finalize(info.Type); - } - - void finalize(TemplateInfo& info) - { - finalize(info.Args); - finalize(info.Params); - finalize(info.Primary); - } - - void finalize(TypeInfo& type) - { - finalize(type.innerType()); - - visit(type, [this](Ty& T) - { - if constexpr(requires { T.ParentType; }) - finalize(T.ParentType); - - if constexpr(Ty::isNamed()) - finalize(T.Name); - - if constexpr(Ty::isAuto()) - finalize(T.Constraint); - }); - } - - void finalize(NameInfo& name) - { - visit(name, [this](Ty& T) - { - finalize(T.Prefix); - - if constexpr(requires { T.TemplateArgs; }) - finalize(T.TemplateArgs); - - finalize(T.id); - }); - } - - void finalize(doc::Node& node) - { - visit(node, [&](NodeTy& N) - { - if constexpr(requires { N.children; }) - finalize(N.children); - - if constexpr(std::derived_from) - { -#if 0 - // This warning shouldn't be triggered if the symbol has - // been explicitly marked excluded in mrdocs.yml - if(! resolveReference(N)) - { - report::warn("Failed to resolve reference to '{}' from '{}'", - N.string, current_->Name); - } -#else - resolveReference(N); -#endif - } - }); - } - - void finalize(Javadoc& javadoc) - { - finalize(javadoc.getBlocks()); - } - - template - void finalize(Optional& val) requires - requires { this->finalize(*val); } - { - if(val) - finalize(*val); - } - - template - void finalize(T* ptr) requires - // KRYSTIAN NOTE: msvc doesn't treat finalize as a dependent - // name unless part of a class member access... - requires { this->finalize(*ptr); } - { - if (ptr) - { - finalize(*ptr); - } - } - - template - void finalize(std::unique_ptr& ptr) requires - requires { this->finalize(*ptr); } - { - if (ptr) - { - finalize(*ptr); - } - } - - template - void finalize(std::optional& ptr) requires - requires { this->finalize(*ptr); } - { - if (ptr) - { - finalize(*ptr); - } - } - - template - void finalize(PolymorphicValue& v) requires - requires { this->finalize(*v); } - { - if (v) - { - finalize(*v); - } - } - - template - requires std::ranges::input_range - void finalize(Range&& range) - { - for (auto&& elem: range) - { - finalize(elem); - } - } - - // ---------------------------------------------------------------- - - void - check(const SymbolID& id) - { - MRDOCS_ASSERT(info_.contains(id)); - } - - void - check(const std::vector& ids) - { - MRDOCS_ASSERT(std::all_of(ids.begin(), ids.end(), - [this](const SymbolID& id) - { - return info_.contains(id); - })); - } - - -public: - Finalizer( - InfoSet& Info, - SymbolLookup& Lookup) - : info_(Info) - , lookup_(Lookup) - { - } - - void finalize(Info& I) - { - current_ = &I; - visit(I, *this); - } - - void - operator()(NamespaceInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - check(I.Members); - finalize(I.javadoc); - finalize(I.UsingDirectives); - // finalize(I.Specializations); - } - - void - operator()(RecordInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - check(I.Members); - finalize(I.javadoc); - // finalize(I.Specializations); - finalize(I.Template); - finalize(I.Bases); - } - - void - operator()(SpecializationInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - check(I.Members); - finalize(I.javadoc); - finalize(I.Primary); - finalize(I.Args); - } - - void - operator()(FunctionInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Template); - finalize(I.ReturnType); - finalize(I.Params); - } - - void - operator()(TypedefInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Template); - finalize(I.Type); - } - - void - operator()(EnumInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - check(I.Members); - finalize(I.javadoc); - finalize(I.UnderlyingType); - } - - void - operator()(FieldInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Type); - } - - void - operator()(VariableInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Template); - finalize(I.Type); - } - - void - operator()(FriendInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.FriendSymbol); - finalize(I.FriendType); - } - - void - operator()(NamespaceAliasInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.AliasedSymbol); - } - - void - operator()(UsingInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Qualifier); - finalize(I.UsingSymbols); - } - - void - operator()(EnumConstantInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - } + MRDOCS_CHECK_OR(config->inheritBaseMembers != PublicSettings::BaseMemberInheritance::Never); + BaseMembersFinalizer baseMembersFinalizer(Info, config); + auto const globalIt = Info.find(SymbolID::global); + MRDOCS_CHECK_OR(globalIt != Info.end()); + baseMembersFinalizer(*dynamic_cast(globalIt->get())); +} - void - operator()(GuideInfo& I) - { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Template); - finalize(I.Deduced); - finalize(I.Params); - } +void +finalizeOverloads(InfoSet& Info, Config const& config) +{ + MRDOCS_CHECK_OR(config->overloads); + OverloadsFinalizer baseMembersFinalizer(Info); + auto const globalIt = Info.find(SymbolID::global); + MRDOCS_CHECK_OR(globalIt != Info.end()); + baseMembersFinalizer(*dynamic_cast(globalIt->get())); +} - void - operator()(ConceptInfo& I) +void +finalizeReferences(InfoSet& Info, SymbolLookup& Lookup) +{ + ReferenceFinalizer visitor(Info, Lookup); + for (auto& I : Info) { - if (I.Parent) - { - check(I.Parent); - } - finalize(I.javadoc); - finalize(I.Template); + MRDOCS_ASSERT(I); + visitor.finalize(*I); } -}; +} +} /** Finalizes a set of Info. @@ -488,15 +58,12 @@ class Finalizer References which should always be valid are not checked. */ void -finalize(InfoSet& Info, SymbolLookup& Lookup) +finalize(CorpusImpl& corpus) { - Finalizer visitor(Info, Lookup); - for(auto& I : Info) - { - MRDOCS_ASSERT(I); - visitor.finalize(*I); - } + finalizeBaseMembers(corpus.info_, *corpus.config_); + finalizeOverloads(corpus.info_, *corpus.config_); + auto const lookup = std::make_unique(corpus); + finalizeReferences(corpus.info_, *lookup); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Finalize.hpp b/src/lib/Metadata/Finalize.hpp index 97e4090c89..7eb033d910 100644 --- a/src/lib/Metadata/Finalize.hpp +++ b/src/lib/Metadata/Finalize.hpp @@ -13,12 +13,13 @@ #include "lib/Lib/Info.hpp" #include "lib/Lib/Lookup.hpp" +#include "lib/Lib/CorpusImpl.hpp" namespace clang::mrdocs { MRDOCS_DECL void -finalize(InfoSet& Info, SymbolLookup& Lookup); +finalize(CorpusImpl& corpus); } // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp new file mode 100644 index 0000000000..454382f8e8 --- /dev/null +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp @@ -0,0 +1,230 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "BaseMembersFinalizer.hpp" +#include "lib/Support/NameParser.hpp" + +namespace clang::mrdocs { + +void +BaseMembersFinalizer:: +inheritBaseMembers(RecordInfo& I, RecordInfo const& B, AccessKind const A) +{ + inheritBaseMembers(I.id, I.Interface, B.Interface, A); +} + +void +BaseMembersFinalizer:: +inheritBaseMembers( + SymbolID const& derivedId, + RecordInterface& derived, + RecordInterface const& base, + AccessKind const A) +{ + if (A == AccessKind::Public) + { + // When a class uses public member access specifier to derive from a + // base, all public members of the base class are accessible as public + // members of the derived class and all protected members of the base + // class are accessible as protected members of the derived class. + // Private members of the base are never accessible unless friended. + inheritBaseMembers(derivedId, derived.Public, base.Public); + inheritBaseMembers(derivedId, derived.Protected, base.Protected); + } + else if (A == AccessKind::Protected) + { + // When a class uses protected member access specifier to derive from a + // base, all public and protected members of the base class are + // accessible as protected members of the derived class (private members + // of the base are never accessible unless friended). + inheritBaseMembers(derivedId, derived.Protected, base.Public); + inheritBaseMembers(derivedId, derived.Protected, base.Protected); + } + else if (A == AccessKind::Private && config_->privateMembers) + { + // When a class uses private member access specifier to derive from a + // base, all public and protected members of the base class are + // accessible as private members of the derived class (private members + // of the base are never accessible unless friended). + inheritBaseMembers(derivedId, derived.Private, base.Public); + inheritBaseMembers(derivedId, derived.Private, base.Protected); + } +} + +void +BaseMembersFinalizer:: +inheritBaseMembers( + SymbolID const& derivedId, + RecordTranche& derived, + RecordTranche const& base) +{ + inheritBaseMembers(derivedId, derived.NamespaceAliases, base.NamespaceAliases); + inheritBaseMembers(derivedId, derived.Typedefs, base.Typedefs); + inheritBaseMembers(derivedId, derived.Records, base.Records); + inheritBaseMembers(derivedId, derived.Enums, base.Enums); + inheritBaseMembers(derivedId, derived.Functions, base.Functions); + inheritBaseMembers(derivedId, derived.StaticFunctions, base.StaticFunctions); + inheritBaseMembers(derivedId, derived.Variables, base.Variables); + inheritBaseMembers(derivedId, derived.StaticVariables, base.StaticVariables); + inheritBaseMembers(derivedId, derived.Concepts, base.Concepts); + inheritBaseMembers(derivedId, derived.Guides, base.Guides); + inheritBaseMembers(derivedId, derived.Friends, base.Friends); +} + +void +BaseMembersFinalizer:: +inheritBaseMembers( + SymbolID const& derivedId, + std::vector& derived, + std::vector const& base) +{ + for (SymbolID const& otherID: base) + { + // Find the info from the base class + auto idIt = std::ranges::find(derived, otherID); + MRDOCS_CHECK_OR_CONTINUE(idIt == derived.end()); + auto otherInfoIt = info_.find(otherID); + MRDOCS_CHECK_OR_CONTINUE(otherInfoIt != info_.end()); + Info& otherInfo = *otherInfoIt->get(); + + // Check if derived class has a member that shadows the base member + auto shadowIt = std::ranges::find_if( + derived, + [&](SymbolID const& id) + { + auto infoIt = info_.find(id); + MRDOCS_CHECK_OR(infoIt != info_.end(), false); + auto& info = *infoIt->get(); + MRDOCS_CHECK_OR(info.Kind == otherInfo.Kind, false); + if (info.isFunction()) + { + // If it's a function, it's only a shadow if the signatures + // are the same + auto const& otherFunc = static_cast(otherInfo); + auto const& func = static_cast(info); + return + std::tie(func.Name, func.Params, func.Template) == + std::tie(otherFunc.Name, otherFunc.Params, func.Template); + } + else + { + // For other kinds of members, it's a shadow if the names + // are the same + return info.Name == otherInfo.Name; + } + }); + MRDOCS_CHECK_OR_CONTINUE(shadowIt == derived.end()); + + bool const copyMember = + config_->inheritBaseMembers == PublicSettings::BaseMemberInheritance::CopyDependencies ? + otherInfo.Extraction == ExtractionMode::Dependency : + config_->inheritBaseMembers == PublicSettings::BaseMemberInheritance::Copy; + + // Not a shadow, so inherit the base member + if (!copyMember) + { + derived.push_back(otherID); + } + else + { + std::unique_ptr otherCopy = + visit(otherInfo, [&](T const& other) + -> std::unique_ptr + { + return std::make_unique(other); + }); + otherCopy->Parent = derivedId; + otherCopy->id = SymbolID::createFromString( + fmt::format( + "{}-{}", + toBase16Str(otherCopy->Parent), + toBase16Str(otherInfo.id))); + derived.push_back(otherCopy->id); + // Get the extraction mode from the derived class + if (otherCopy->Extraction == ExtractionMode::Dependency) + { + auto derivedInfoIt = info_.find(derivedId); + MRDOCS_CHECK_OR_CONTINUE(derivedInfoIt != info_.end()); + Info const& derivedInfo = **derivedInfoIt; + otherCopy->Extraction = derivedInfo.Extraction; + } + info_.insert(std::move(otherCopy)); + } + } +} + +void +BaseMembersFinalizer:: +finalizeRecords(std::vector const& ids) +{ + for (SymbolID const& id: ids) + { + auto infoIt = info_.find(id); + MRDOCS_CHECK_OR_CONTINUE(infoIt != info_.end()); + auto& info = *infoIt; + auto* record = dynamic_cast(info.get()); + MRDOCS_CHECK_OR_CONTINUE(record); + operator()(*record); + } +} + +void +BaseMembersFinalizer:: +finalizeNamespaces(std::vector const& ids) +{ + for (SymbolID const& id: ids) + { + auto infoIt = info_.find(id); + MRDOCS_CHECK_OR_CONTINUE(infoIt != info_.end()); + auto& info = *infoIt; + auto* ns = dynamic_cast(info.get()); + MRDOCS_CHECK_OR_CONTINUE(ns); + operator()(*ns); + } +} + +void +BaseMembersFinalizer:: +operator()(NamespaceInfo& I) +{ + report::trace("Extracting base members for namespace '{}'", I.Name); + finalizeRecords(I.Members.Records); + finalizeNamespaces(I.Members.Namespaces); +} + +void +BaseMembersFinalizer:: +operator()(RecordInfo& I) +{ + report::trace("Extracting base members for record '{}'", I.Name); + MRDOCS_CHECK_OR(!finalized_.contains(I.id)); + for (BaseInfo const& baseI: I.Bases) + { + auto const* baseNameType = get(baseI.Type); + MRDOCS_CHECK_OR_CONTINUE(baseNameType); + auto const* baseName = get(baseNameType->Name); + MRDOCS_CHECK_OR_CONTINUE(baseName); + SymbolID const baseID = baseName->id; + MRDOCS_CHECK_OR_CONTINUE(baseID); + auto baseIt = info_.find(baseID); + MRDOCS_CHECK_OR_CONTINUE(baseIt != info_.end()); + std::unique_ptr const& base = *baseIt; + auto* baseRecord = dynamic_cast(base.get()); + MRDOCS_CHECK_OR_CONTINUE(baseRecord); + operator()(*baseRecord); + inheritBaseMembers(I, *baseRecord, baseI.Access); + } + finalizeRecords(I.Interface.Public.Records); + finalizeRecords(I.Interface.Protected.Records); + finalizeRecords(I.Interface.Private.Records); + finalized_.emplace(I.id); +} + +} // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp new file mode 100644 index 0000000000..a3aa35d13d --- /dev/null +++ b/src/lib/Metadata/Finalizers/BaseMembersFinalizer.hpp @@ -0,0 +1,81 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_METADATA_FINALIZER_BASEMEMBERSFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZER_BASEMEMBERSFINALIZER_HPP + +#include "lib/Lib/Info.hpp" +#include "lib/Lib/Lookup.hpp" + +namespace clang::mrdocs { + +/** Finalizes a set of Info. + + This removes any references to SymbolIDs + which do not exist. + + References which should always be valid + are not checked. +*/ +class BaseMembersFinalizer +{ + InfoSet& info_; + Config const& config_; + std::unordered_set finalized_; + + void + inheritBaseMembers(RecordInfo& I, RecordInfo const& B, AccessKind A); + + void + inheritBaseMembers( + SymbolID const& derivedId, + RecordInterface& derived, + RecordInterface const& base, + AccessKind A); + + void + inheritBaseMembers( + SymbolID const& derivedId, + RecordTranche& derived, + RecordTranche const& base); + + void + inheritBaseMembers( + SymbolID const& derivedId, + std::vector& derived, + std::vector const& base); + + void + finalizeRecords(std::vector const& ids); + + void + finalizeNamespaces(std::vector const& ids); + +public: + BaseMembersFinalizer( + InfoSet& Info, + Config const& config) + : info_(Info) + , config_(config) + {} + + void + operator()(NamespaceInfo& I); + + void + operator()(RecordInfo& I); + + void + operator()(Info& I) {} +}; + +} // clang::mrdocs + +#endif diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp new file mode 100644 index 0000000000..331b528768 --- /dev/null +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.cpp @@ -0,0 +1,124 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "OverloadsFinalizer.hpp" +#include "lib/Support/NameParser.hpp" + +namespace clang::mrdocs { + +void +OverloadsFinalizer:: +foldRecordMembers(std::vector const& ids) +{ + for (SymbolID const& id: ids) + { + auto infoIt = info_.find(id); + MRDOCS_CHECK_OR_CONTINUE(infoIt != info_.end()); + auto& info = *infoIt; + auto* record = dynamic_cast(info.get()); + MRDOCS_CHECK_OR_CONTINUE(record); + operator()(*record); + } +} + +void +OverloadsFinalizer:: +foldNamespaceMembers(std::vector const& ids) +{ + for (SymbolID const& id: ids) + { + auto infoIt = info_.find(id); + MRDOCS_CHECK_OR_CONTINUE(infoIt != info_.end()); + auto& info = *infoIt; + auto* ns = dynamic_cast(info.get()); + MRDOCS_CHECK_OR_CONTINUE(ns); + operator()(*ns); + } +} + +void +OverloadsFinalizer:: +foldOverloads(SymbolID const& parent, std::vector& ids) +{ + for (auto it = ids.begin(); it != ids.end(); ++it) + { + // Get the FunctionInfo for the current id + auto infoIt = info_.find(*it); + MRDOCS_CHECK_OR_CONTINUE(infoIt != info_.end()); + auto& info = *infoIt; + auto* function = dynamic_cast(info.get()); + MRDOCS_CHECK_OR_CONTINUE(function); + + // Check if the FunctionInfo is unique + auto sameNameIt = + std::find_if( + it + 1, + ids.end(), + [&](SymbolID const& otherID) + { + auto const otherInfoIt = info_.find(otherID); + MRDOCS_CHECK_OR(otherInfoIt != info_.end(), false); + auto& otherInfo = *otherInfoIt; + return function->Name == otherInfo->Name; + }); + if (sameNameIt == ids.end()) + { + continue; + } + + // FunctionInfo is not unique, so merge it with the other FunctionInfos + // into an OverloadsInfo + OverloadsInfo O(parent, function->Name); + addMember(O, *function); + *it = O.id; + auto const itOffset = it - ids.begin(); + for (auto otherIt = it + 1; otherIt != ids.end(); ++otherIt) + { + auto otherInfoIt = info_.find(*otherIt); + MRDOCS_CHECK_OR_CONTINUE(otherInfoIt != info_.end()); + auto& otherInfo = *otherInfoIt; + auto* otherFunction = dynamic_cast(otherInfo.get()); + MRDOCS_CHECK_OR_CONTINUE(otherFunction); + if (function->Name == otherFunction->Name) + { + addMember(O, *otherFunction); + otherIt = std::prev(ids.erase(otherIt)); + } + } + it = ids.begin() + itOffset; + info_.emplace(std::make_unique(std::move(O))); + } +} + +void +OverloadsFinalizer:: +operator()(NamespaceInfo& I) +{ + foldOverloads(I.id, I.Members.Functions); + foldRecordMembers(I.Members.Records); + foldNamespaceMembers(I.Members.Namespaces); +} + +void +OverloadsFinalizer:: +operator()(RecordInfo& I) +{ + foldOverloads(I.id, I.Interface.Public.Functions); + foldOverloads(I.id, I.Interface.Protected.Functions); + foldOverloads(I.id, I.Interface.Private.Functions); + foldOverloads(I.id, I.Interface.Public.StaticFunctions); + foldOverloads(I.id, I.Interface.Protected.StaticFunctions); + foldOverloads(I.id, I.Interface.Private.StaticFunctions); + foldRecordMembers(I.Interface.Public.Records); + foldRecordMembers(I.Interface.Protected.Records); + foldRecordMembers(I.Interface.Private.Records); +} + +} // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp new file mode 100644 index 0000000000..3bfa1bb074 --- /dev/null +++ b/src/lib/Metadata/Finalizers/OverloadsFinalizer.hpp @@ -0,0 +1,57 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_METADATA_FINALIZER_OVERLOADSFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZER_OVERLOADSFINALIZER_HPP + +#include "lib/Lib/Info.hpp" +#include "lib/Lib/Lookup.hpp" + +namespace clang::mrdocs { + +/** Finalizes a set of Info. + + This removes any references to SymbolIDs + which do not exist. + + References which should always be valid + are not checked. +*/ +class OverloadsFinalizer +{ + InfoSet& info_; + + void + foldRecordMembers(std::vector const& ids); + + void + foldNamespaceMembers(std::vector const& ids); + + void + foldOverloads(SymbolID const& parent, std::vector& ids); + +public: + OverloadsFinalizer(InfoSet& Info) + : info_(Info) + {} + + void + operator()(NamespaceInfo& I); + + void + operator()(RecordInfo& I); + + void + operator()(Info& I) {} +}; + +} // clang::mrdocs + +#endif diff --git a/src/lib/Metadata/Finalizers/ReferenceFinalizer.cpp b/src/lib/Metadata/Finalizers/ReferenceFinalizer.cpp new file mode 100644 index 0000000000..271cd29226 --- /dev/null +++ b/src/lib/Metadata/Finalizers/ReferenceFinalizer.cpp @@ -0,0 +1,471 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "ReferenceFinalizer.hpp" +#include "lib/Support/NameParser.hpp" + +namespace clang::mrdocs { + +/** Finalizes a set of Info. + + This removes any references to SymbolIDs + which do not exist. + + References which should always be valid + are not checked. +*/ +bool +ReferenceFinalizer:: +resolveReference(doc::Reference& ref) const +{ + auto parse_result = parseIdExpression(ref.string); + if (!parse_result) + { + return false; + } + + if (parse_result->name.empty()) + { + return false; + } + + auto is_acceptable = [&](Info const& I) -> bool + { + // if we are copying the documentation of the + // referenced symbol, ignore the current declaration + if (ref.kind == doc::Kind::copied) + { + return &I != current_; + } + // otherwise, consider the result to be acceptable + return true; + }; + + Info const* found = nullptr; + if(parse_result->qualified) + { + Info* context = current_; + std::vector qualifier; + // KRYSTIAN FIXME: lookupQualified should accept + // std::vector as the qualifier + for (auto& part: parse_result->qualifier) + { + qualifier.push_back(part); + } + if(parse_result->qualifier.empty()) + { + MRDOCS_ASSERT(info_.contains(SymbolID::global)); + context = info_.find(SymbolID::global)->get(); + } + found = lookup_.lookupQualified( + context, + qualifier, + parse_result->name, + is_acceptable); + } + else + { + found = lookup_.lookupUnqualified( + current_, + parse_result->name, + is_acceptable); + } + + // prevent recursive documentation copies + if (ref.kind == doc::Kind::copied && + found && + found->id == current_->id) + { + return false; + } + + // if we found a symbol, replace the reference + // ID with the SymbolID of that symbol + if (found) + { + ref.id = found->id; + } + return found; +} + +void +ReferenceFinalizer:: +finalize(SymbolID& id) +{ + if (id && !info_.contains(id)) + { + id = SymbolID::invalid; + } +} + +void +ReferenceFinalizer:: +finalize(std::vector& ids) +{ + std::erase_if(ids, [this](SymbolID const& id) + { + return !id || ! info_.contains(id); + }); +} + +void +ReferenceFinalizer:: +finalize(TArg& arg) +{ + visit(arg, [this](Ty& A) + { + if constexpr (Ty::isType()) + { + finalize(A.Type); + } + if constexpr (Ty::isTemplate()) + { + finalize(A.Template); + } + }); +} + +void +ReferenceFinalizer:: +finalize(TParam& param) +{ + finalize(param.Default); + + visit(param, [this](Ty& P) + { + if constexpr (Ty::isType()) + { + finalize(P.Constraint); + } + + if constexpr (Ty::isNonType()) + { + finalize(P.Type); + } + + if constexpr (Ty::isTemplate()) + { + finalize(P.Params); + } + }); +} + +void +ReferenceFinalizer:: +finalize(Param& param) +{ + finalize(param.Type); +} + +void +ReferenceFinalizer:: +finalize(BaseInfo& info) +{ + finalize(info.Type); +} + +void +ReferenceFinalizer:: +finalize(TemplateInfo& info) +{ + finalize(info.Args); + finalize(info.Params); + finalize(info.Primary); +} + +void +ReferenceFinalizer:: +finalize(TypeInfo& type) +{ + finalize(type.innerType()); + + visit(type, [this](Ty& T) + { + if constexpr (requires { T.ParentType; }) + { + finalize(T.ParentType); + } + + if constexpr (Ty::isNamed()) + { + finalize(T.Name); + } + + if constexpr (Ty::isAuto()) + { + finalize(T.Constraint); + } + }); +} + +void +ReferenceFinalizer:: +finalize(NameInfo& name) +{ + visit(name, [this](Ty& T) + { + finalize(T.Prefix); + + if constexpr(requires { T.TemplateArgs; }) + finalize(T.TemplateArgs); + + finalize(T.id); + }); +} + +void +ReferenceFinalizer:: +finalize(doc::Node& node) +{ + visit(node, [&](NodeTy& N) + { + if constexpr (requires { N.children; }) + { + finalize(N.children); + } + + if constexpr(std::derived_from) + { + if (!resolveReference(N)) + { + // The warning shouldn't be triggered if the symbol name + // has been explicitly marked excluded in mrdocs.yml. + // Finalizer needs to be updated to handle this case. + // When tagfile support is implemented, we can't + // report an error if the reference exists in the tagfile. + // if constexpr (false) + // { + // report::warn( + // "Failed to resolve reference to '{}' from '{}'", + // N.string, + // current_->Name); + // } + } + } + }); +} + +void +ReferenceFinalizer:: +finalize(Javadoc& javadoc) +{ + finalize(javadoc.getBlocks()); +} + +void +ReferenceFinalizer:: +check(SymbolID const& id) const +{ + MRDOCS_ASSERT(info_.contains(id)); +} + +void +ReferenceFinalizer:: +finalize(Info& I) +{ + current_ = &I; + visit(I, *this); +} + +void +ReferenceFinalizer:: +operator()(NamespaceInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + check(allMembers(I)); + finalize(I.javadoc); + finalize(I.UsingDirectives); +} + +void +ReferenceFinalizer:: +operator()(RecordInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + check(allMembers(I)); + finalize(I.javadoc); + // finalize(I.Specializations); + finalize(I.Template); + finalize(I.Bases); +} + +void +ReferenceFinalizer:: +operator()(SpecializationInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Primary); + finalize(I.Args); +} + +void +ReferenceFinalizer:: +operator()(FunctionInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Template); + finalize(I.ReturnType); + finalize(I.Params); +} + +void +ReferenceFinalizer:: +operator()(TypedefInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Template); + finalize(I.Type); +} + +void +ReferenceFinalizer:: +operator()(EnumInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + check(allMembers(I)); + finalize(I.javadoc); + finalize(I.UnderlyingType); +} + +void +ReferenceFinalizer:: +operator()(FieldInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Type); +} + +void +ReferenceFinalizer:: +operator()(VariableInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Template); + finalize(I.Type); +} + +void +ReferenceFinalizer:: +operator()(FriendInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.FriendSymbol); + finalize(I.FriendType); +} + +void +ReferenceFinalizer:: +operator()(NamespaceAliasInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.AliasedSymbol); +} + +void +ReferenceFinalizer:: +operator()(UsingInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Qualifier); + finalize(I.UsingSymbols); +} + +void +ReferenceFinalizer:: +operator()(EnumConstantInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); +} + +void +ReferenceFinalizer:: +operator()(GuideInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Template); + finalize(I.Deduced); + finalize(I.Params); +} + +void +ReferenceFinalizer:: +operator()(ConceptInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + finalize(I.javadoc); + finalize(I.Template); +} + +void +ReferenceFinalizer:: +operator()(OverloadsInfo& I) +{ + if (I.Parent) + { + check(I.Parent); + } + check(allMembers(I)); + finalize(I.javadoc); +} + +} // clang::mrdocs diff --git a/src/lib/Metadata/Finalizers/ReferenceFinalizer.hpp b/src/lib/Metadata/Finalizers/ReferenceFinalizer.hpp new file mode 100644 index 0000000000..2eced7fceb --- /dev/null +++ b/src/lib/Metadata/Finalizers/ReferenceFinalizer.hpp @@ -0,0 +1,210 @@ +// +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_METADATA_FINALIZER_REFERENCEFINALIZER_HPP +#define MRDOCS_LIB_METADATA_FINALIZER_REFERENCEFINALIZER_HPP + +#include "lib/Lib/Info.hpp" +#include "lib/Lib/Lookup.hpp" + +namespace clang::mrdocs { + +/** Finalizes a set of Info. + + This removes any references to SymbolIDs + which do not exist. + + References which should always be valid + are not checked. +*/ +class ReferenceFinalizer +{ + InfoSet& info_; + SymbolLookup& lookup_; + Info* current_ = nullptr; + + bool + resolveReference(doc::Reference& ref) const; + + void + finalize(SymbolID& id); + + void + finalize(std::vector& ids); + + void + finalize(TArg& arg); + + void + finalize(TParam& param); + + void + finalize(Param& param); + + void + finalize(BaseInfo& info); + + void + finalize(TemplateInfo& info); + + void + finalize(TypeInfo& type); + + void + finalize(NameInfo& name); + + void + finalize(doc::Node& node); + + void + finalize(Javadoc& javadoc); + + template + void + finalize(Optional& val) requires + requires { this->finalize(*val); } + { + if (val) + { + finalize(*val); + } + } + + template + void + finalize(T* ptr) requires + requires { this->finalize(*ptr); } + { + if (ptr) + { + finalize(*ptr); + } + } + + template + void + finalize(std::unique_ptr& ptr) requires + requires { this->finalize(*ptr); } + { + if (ptr) + { + finalize(*ptr); + } + } + + template + void finalize(std::optional& ptr) requires + requires { this->finalize(*ptr); } + { + if (ptr) + { + finalize(*ptr); + } + } + + template + void finalize(PolymorphicValue& v) requires + requires { this->finalize(*v); } + { + if (v) + { + finalize(*v); + } + } + + template + requires std::ranges::input_range + void finalize(Range&& range) + { + for (auto&& elem: range) + { + finalize(elem); + } + } + + // ---------------------------------------------------------------- + + // Check if SymbolID exists in info_ + void + check(SymbolID const& id) const; + + template T> + void + check(T&& ids) const + { + MRDOCS_ASSERT(std::ranges::all_of(ids, + [this](const SymbolID& id) + { + return info_.contains(id); + })); + } + + +public: + ReferenceFinalizer( + InfoSet& Info, + SymbolLookup& Lookup) + : info_(Info) + , lookup_(Lookup) + { + } + + void finalize(Info& I); + + void + operator()(NamespaceInfo& I); + + void + operator()(RecordInfo& I); + + void + operator()(SpecializationInfo& I); + + void + operator()(FunctionInfo& I); + + void + operator()(TypedefInfo& I); + + void + operator()(EnumInfo& I); + + void + operator()(FieldInfo& I); + + void + operator()(VariableInfo& I); + + void + operator()(FriendInfo& I); + + void + operator()(NamespaceAliasInfo& I); + + void + operator()(UsingInfo& I); + + void + operator()(EnumConstantInfo& I); + + void + operator()(GuideInfo& I); + + void + operator()(ConceptInfo& I); + + void + operator()(OverloadsInfo& I); +}; + +} // clang::mrdocs + +#endif diff --git a/src/lib/Metadata/Info.cpp b/src/lib/Metadata/Info.cpp index 6fffa7ff5f..ad57ddb037 100644 --- a/src/lib/Metadata/Info.cpp +++ b/src/lib/Metadata/Info.cpp @@ -5,23 +5,23 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Dom/LazyArray.hpp" -#include "lib/Dom/LazyObject.hpp" +#include +#include #include "lib/Support/Radix.hpp" -#include #include #include #include #include +#include #include -#include +#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { dom::String toString(InfoKind kind) noexcept @@ -36,260 +36,34 @@ toString(InfoKind kind) noexcept } } -/* Customization to map Info types to DOM objects - - This function maps an Info type to a DOM object. - It includes all members of the derived type. - - The traits store a reference to the DomCorpus - so that it can resolve symbol IDs to the corresponding - Info objects. - - Whenever a member refers to symbol IDs, - the mapping trait will automatically resolve the - symbol ID to the corresponding Info object. - - This allows all references to be resolved to the - corresponding Info object lazily from the templates - that use the DOM. - */ -template -requires std::derived_from void -tag_invoke( - dom::LazyObjectMapTag, - IO& io, - InfoTy const& I, - DomCorpus const* domCorpus) +merge(Info& I, Info&& Other) { - MRDOCS_ASSERT(domCorpus); - io.map("id", I.id); - if (!I.Name.empty()) + MRDOCS_ASSERT(I.id); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (I.Name == "") { - io.map("name", I.Name); + I.Name = Other.Name; } - io.map("kind", I.Kind); - io.map("access", I.Access); - io.map("extraction", I.Extraction); - io.map("isRegular", I.Extraction == ExtractionMode::Regular); - io.map("isSeeBelow", I.Extraction == ExtractionMode::SeeBelow); - io.map("isImplementationDefined", I.Extraction == ExtractionMode::ImplementationDefined); - io.map("isDependency", I.Extraction == ExtractionMode::Dependency); if (I.Parent) { - io.map("parent", I.Parent); - io.defer("parents", [&] - { - // A convenient list to iterate over the parents - // with resorting to partial template recursion - Corpus const& corpus = domCorpus->getCorpus(); - auto pIds = getParents(corpus, I); - dom::Array res; - for (auto const& id : pIds) - { - Info const& PI = corpus.get(id); - res.push_back(domCorpus->construct(PI)); - } - return res; - }); - } - if (I.javadoc) - { - io.map("doc", *I.javadoc); - } - using T = std::remove_cvref_t; - if constexpr(std::derived_from) - { - io.map("members", dom::LazyArray(I.Members, domCorpus)); - io.defer("overloads", [&]{ - // Eager array with overloadset or symbol - return generateScopeOverloadsArray(I, *domCorpus); - }); - } - if constexpr(std::derived_from) - { - io.map("loc", static_cast(I)); - } - if constexpr(T::isNamespace()) - { - io.defer("interface", [&I, domCorpus]{ - // Eager object with each Info type - auto t = std::make_shared(makeTranche(I, **domCorpus)); - return dom::ValueFrom(t, domCorpus); - }); - io.map("usingDirectives", dom::LazyArray(I.UsingDirectives, domCorpus)); - } - if constexpr (T::isRecord()) - { - io.map("tag", I.KeyKind); - io.map("defaultAccess", getDefaultAccessString(I.KeyKind)); - io.map("isTypedef", I.IsTypeDef); - io.map("bases", dom::LazyArray(I.Bases, domCorpus)); - io.defer("interface", [domCorpus, &I] { - // Eager object with each Info type for each access specifier - auto sp = std::make_shared(makeInterface(I, domCorpus->getCorpus())); - return dom::ValueFrom(sp, domCorpus); - }); - io.map("template", I.Template); - } - if constexpr (T::isEnum()) - { - io.map("type", I.UnderlyingType); - io.map("isScoped", I.Scoped); - } - if constexpr (T::isFunction()) - { - io.map("isVariadic", I.IsVariadic); - io.map("isVirtual", I.IsVirtual); - io.map("isVirtualAsWritten", I.IsVirtualAsWritten); - io.map("isPure", I.IsPure); - io.map("isDefaulted", I.IsDefaulted); - io.map("isExplicitlyDefaulted", I.IsExplicitlyDefaulted); - io.map("isDeleted", I.IsDeleted); - io.map("isDeletedAsWritten", I.IsDeletedAsWritten); - io.map("isNoReturn", I.IsNoReturn); - io.map("hasOverrideAttr", I.HasOverrideAttr); - io.map("hasTrailingReturn", I.HasTrailingReturn); - io.map("isConst", I.IsConst); - io.map("isVolatile", I.IsVolatile); - io.map("isFinal", I.IsFinal); - io.map("isNodiscard", I.IsNodiscard); - io.map("isExplicitObjectMemberFunction", I.IsExplicitObjectMemberFunction); - if (I.Constexpr != ConstexprKind::None) - { - io.map("constexprKind", I.Constexpr); - } - if (I.StorageClass != StorageClassKind::None) - { - io.map("storageClass", I.StorageClass); - } - if (I.RefQualifier != ReferenceKind::None) - { - io.map("refQualifier", I.RefQualifier); - } - io.map("class", I.Class); - io.map("params", dom::LazyArray(I.Params, domCorpus)); - io.map("return", I.ReturnType); - io.map("template", I.Template); - io.map("overloadedOperator", I.OverloadedOperator); - io.map("exceptionSpec", I.Noexcept); - io.map("explicitSpec", I.Explicit); - if (!I.Requires.Written.empty()) - { - io.map("requires", I.Requires.Written); - } - io.map("attributes", dom::LazyArray(I.Attributes)); + I.Parent = Other.Parent; } - if constexpr (T::isTypedef()) + if (I.Access == AccessKind::None) { - io.map("type", I.Type); - io.map("template", I.Template); - io.map("isUsing", I.IsUsing); + I.Access = Other.Access; } - if constexpr (T::isVariable()) - { - auto const& U = static_cast(I); - io.map("type", U.Type); - io.map("template", U.Template); - if (U.StorageClass != StorageClassKind::None) - { - io.map("storageClass", U.StorageClass); - } - io.map("isInline", U.IsInline); - io.map("isConstexpr", U.IsConstexpr); - io.map("isConstinit", U.IsConstinit); - io.map("isThreadLocal", U.IsThreadLocal); - if (!U.Initializer.Written.empty()) - { - io.map("initializer", U.Initializer.Written); - } - io.map("attributes", dom::LazyArray(U.Attributes)); - } - if constexpr (T::isField()) - { - io.map("type", I.Type); - if (!I.Default.Written.empty()) - { - io.map("default", I.Default.Written); - } - io.map("isMaybeUnused", I.IsMaybeUnused); - io.map("isDeprecated", I.IsDeprecated); - io.map("isVariant", I.IsVariant); - io.map("isMutable", I.IsMutable); - io.map("isBitfield", I.IsBitfield); - io.map("hasNoUniqueAddress", I.HasNoUniqueAddress); - if (I.IsBitfield) - { - io.map("bitfieldWidth", I.BitfieldWidth.Written); - } - io.map("attributes", dom::LazyArray(I.Attributes)); - } - if constexpr (T::isSpecialization()) - {} - if constexpr (T::isFriend()) - { - if (I.FriendSymbol) - { - io.defer("name", [&I, domCorpus]{ - return dom::ValueFrom(I.FriendSymbol, domCorpus).get("name"); - }); - io.map("symbol", I.FriendSymbol); - } - else if (I.FriendType) - { - io.defer("name", [&]{ - return dom::ValueFrom(I.FriendType, domCorpus).get("name"); - }); - io.map("type", I.FriendType); - } - } - if constexpr (T::isNamespaceAlias()) - { - MRDOCS_ASSERT(I.AliasedSymbol); - io.map("aliasedSymbol", I.AliasedSymbol); - } - if constexpr (T::isUsing()) - { - io.map("class", I.Class); - io.map("shadows", dom::LazyArray(I.UsingSymbols, domCorpus)); - io.map("qualifier", I.Qualifier); - } - if constexpr (T::isEnumConstant()) + I.Extraction = leastSpecific(I.Extraction, Other.Extraction); + + // Append javadocs + if (!I.javadoc) { - if (!I.Initializer.Written.empty()) - { - io.map("initializer", I.Initializer.Written); - } + I.javadoc = std::move(Other.javadoc); } - if constexpr (T::isGuide()) + else if (Other.javadoc) { - io.map("params", dom::LazyArray(I.Params, domCorpus)); - io.map("deduced", I.Deduced); - io.map("template", I.Template); - io.map("explicitSpec", I.Explicit); + merge(*I.javadoc, std::move(*Other.javadoc)); } - if constexpr (T::isConcept()) - { - io.map("template", I.Template); - if (!I.Constraint.Written.empty()) - { - io.map("constraint", I.Constraint.Written); - } - } -} - -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - Info const& I, - DomCorpus const* domCorpus) -{ - return visit(I, - [&](T const& I) - { - v = dom::LazyObject(I, domCorpus); - }); } bool @@ -339,5 +113,4 @@ operator<( return false; } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Info/Concept.cpp b/src/lib/Metadata/Info/Concept.cpp new file mode 100644 index 0000000000..3a50182baf --- /dev/null +++ b/src/lib/Metadata/Info/Concept.cpp @@ -0,0 +1,30 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(ConceptInfo& I, ConceptInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(I.Constraint.Written.empty()) + I.Constraint = std::move(Other.Constraint); + if (! I.Template) + I.Template = std::move(Other.Template); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Enum.cpp b/src/lib/Metadata/Info/Enum.cpp new file mode 100644 index 0000000000..0deedd7d6e --- /dev/null +++ b/src/lib/Metadata/Info/Enum.cpp @@ -0,0 +1,50 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +namespace { + +void +reduceSymbolIDs( + std::vector& list, + std::vector&& otherList) +{ + for(auto const& id : otherList) + { + if (auto it = llvm::find(list, id); it != list.end()) + { + continue; + } + list.push_back(id); + } +} + +} // (anon) + +void +merge(EnumInfo& I, EnumInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(! I.Scoped) + I.Scoped = Other.Scoped; + if (! I.UnderlyingType) + I.UnderlyingType = std::move(Other.UnderlyingType); + reduceSymbolIDs(I.Constants, std::move(Other.Constants)); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/EnumConstant.cpp b/src/lib/Metadata/Info/EnumConstant.cpp new file mode 100644 index 0000000000..d8199664b1 --- /dev/null +++ b/src/lib/Metadata/Info/EnumConstant.cpp @@ -0,0 +1,28 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(EnumConstantInfo& I, EnumConstantInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(I.Initializer.Written.empty()) + I.Initializer = std::move(Other.Initializer); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Field.cpp b/src/lib/Metadata/Info/Field.cpp new file mode 100644 index 0000000000..f9de78b61c --- /dev/null +++ b/src/lib/Metadata/Info/Field.cpp @@ -0,0 +1,37 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(FieldInfo& I, FieldInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(! I.Type) + I.Type = std::move(Other.Type); + if(I.Default.Written.empty()) + I.Default = std::move(Other.Default); + I.IsBitfield |= Other.IsBitfield; + merge(I.BitfieldWidth, std::move(Other.BitfieldWidth)); + I.IsVariant |= Other.IsVariant; + I.IsMutable |= Other.IsMutable; + I.IsMaybeUnused |= Other.IsMaybeUnused; + I.IsDeprecated |= Other.IsDeprecated; + I.HasNoUniqueAddress |= Other.HasNoUniqueAddress; +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Friend.cpp b/src/lib/Metadata/Info/Friend.cpp new file mode 100644 index 0000000000..dee8b71c84 --- /dev/null +++ b/src/lib/Metadata/Info/Friend.cpp @@ -0,0 +1,30 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(FriendInfo& I, FriendInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(! I.FriendSymbol) + I.FriendSymbol = Other.FriendSymbol; + if(! I.FriendType) + I.FriendType = std::move(Other.FriendType); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Function.cpp b/src/lib/Metadata/Info/Function.cpp index 775b4ed03e..d36dac5b58 100644 --- a/src/lib/Metadata/Info/Function.cpp +++ b/src/lib/Metadata/Info/Function.cpp @@ -11,12 +11,11 @@ // #include +#include #include -#include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { namespace { @@ -172,6 +171,70 @@ tag_invoke( v = dom::LazyObject(p, domCorpus); } -} // mrdocs -} // clang +void +merge(FunctionInfo& I, FunctionInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (I.Class == FunctionClass::Normal) + { + I.Class = Other.Class; + } + if (!I.ReturnType) + { + I.ReturnType = std::move(Other.ReturnType); + } + if (I.Params.empty()) + { + I.Params = std::move(Other.Params); + } + if (!I.Template) + { + I.Template = std::move(Other.Template); + } + if (I.Noexcept.Implicit) + { + I.Noexcept = std::move(Other.Noexcept); + } + if (I.Explicit.Implicit) + { + I.Explicit = std::move(Other.Explicit); + } + merge(I.Requires, std::move(Other.Requires)); + I.IsVariadic |= Other.IsVariadic; + I.IsVirtual |= Other.IsVirtual; + I.IsVirtualAsWritten |= Other.IsVirtualAsWritten; + I.IsPure |= Other.IsPure; + I.IsDefaulted |= Other.IsDefaulted; + I.IsExplicitlyDefaulted |= Other.IsExplicitlyDefaulted; + I.IsDeleted |= Other.IsDeleted; + I.IsDeletedAsWritten |= Other.IsDeletedAsWritten; + I.IsNoReturn |= Other.IsNoReturn; + I.HasOverrideAttr |= Other.HasOverrideAttr; + I.HasTrailingReturn |= Other.HasTrailingReturn; + I.IsConst |= Other.IsConst; + I.IsVolatile |= Other.IsVolatile; + I.IsFinal |= Other.IsFinal; + I.IsNodiscard |= Other.IsNodiscard; + I.IsExplicitObjectMemberFunction |= Other.IsExplicitObjectMemberFunction; + if (I.Constexpr == ConstexprKind::None) + { + I.Constexpr = Other.Constexpr; + } + if (I.StorageClass == StorageClassKind::None) + { + I.StorageClass = Other.StorageClass; + } + if (I.RefQualifier == ReferenceKind::None) + { + I.RefQualifier = Other.RefQualifier; + } + if (I.OverloadedOperator == OperatorKind::None) + { + I.OverloadedOperator = Other.OverloadedOperator; + } +} + + +} // clang::mrdocs diff --git a/src/lib/Metadata/Info/Guide.cpp b/src/lib/Metadata/Info/Guide.cpp new file mode 100644 index 0000000000..4a38d563cf --- /dev/null +++ b/src/lib/Metadata/Info/Guide.cpp @@ -0,0 +1,41 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void merge(GuideInfo& I, GuideInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (!I.Deduced) + { + I.Deduced = std::move(Other.Deduced); + } + if (I.Params.empty()) + { + I.Params = std::move(Other.Params); + } + if (!I.Template) + { + I.Template = std::move(Other.Template); + } + if (I.Explicit.Implicit) + { + I.Explicit = std::move(Other.Explicit); + } +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Namespace.cpp b/src/lib/Metadata/Info/Namespace.cpp new file mode 100644 index 0000000000..b167c60ac8 --- /dev/null +++ b/src/lib/Metadata/Info/Namespace.cpp @@ -0,0 +1,61 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +namespace { +void +reduceSymbolIDs( + std::vector& list, + std::vector&& otherList) +{ + for(auto const& id : otherList) + { + if (auto it = llvm::find(list, id); it != list.end()) + { + continue; + } + list.push_back(id); + } +} +} // (anon) + +void +merge(NamespaceTranche& I, NamespaceTranche&& Other) +{ + reduceSymbolIDs(I.Namespaces, std::move(Other.Namespaces)); + reduceSymbolIDs(I.NamespaceAliases, std::move(Other.NamespaceAliases)); + reduceSymbolIDs(I.Typedefs, std::move(Other.Typedefs)); + reduceSymbolIDs(I.Records, std::move(Other.Records)); + reduceSymbolIDs(I.Enums, std::move(Other.Enums)); + reduceSymbolIDs(I.Functions, std::move(Other.Functions)); + reduceSymbolIDs(I.Variables, std::move(Other.Variables)); + reduceSymbolIDs(I.Concepts, std::move(Other.Concepts)); + reduceSymbolIDs(I.Guides, std::move(Other.Guides)); + reduceSymbolIDs(I.Usings, std::move(Other.Usings)); +} + +void +merge(NamespaceInfo& I, NamespaceInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + merge(I.Members, std::move(Other.Members)); + reduceSymbolIDs(I.UsingDirectives, std::move(Other.UsingDirectives)); + I.IsInline |= Other.IsInline; + I.IsAnonymous |= Other.IsAnonymous; +} +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/NamespaceAlias.cpp b/src/lib/Metadata/Info/NamespaceAlias.cpp new file mode 100644 index 0000000000..24ce9a85a9 --- /dev/null +++ b/src/lib/Metadata/Info/NamespaceAlias.cpp @@ -0,0 +1,28 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (! I.AliasedSymbol) + I.AliasedSymbol = std::move(Other.AliasedSymbol); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Overloads.cpp b/src/lib/Metadata/Info/Overloads.cpp index 4ce7940c8a..06d787efa0 100644 --- a/src/lib/Metadata/Info/Overloads.cpp +++ b/src/lib/Metadata/Info/Overloads.cpp @@ -9,72 +9,55 @@ // #include "lib/Support/Radix.hpp" -#include -#include -#include -#include +#include #include #include #include #include +#include +#include +#include namespace clang::mrdocs { +OverloadsInfo:: +OverloadsInfo(SymbolID const& Parent, std::string_view Name) noexcept + : InfoCommonBase( + SymbolID::createFromString( + fmt::format("{}-{}", toBase16(Parent), Name))) +{ + this->Parent = Parent; +} + void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - OverloadSet const& overloads, - DomCorpus const* domCorpus) +merge(OverloadsInfo& I, OverloadsInfo&& Other) { - /* Unfortunately, this can't use LazyObject like all other - * Corpus types because the overload sets are not directly - * available from the corpus. - * The `overloads` value is a temporary reference created - * by the `Info` tag_invoke. - */ - dom::Object res; - res.set("id", fmt::format("{}-{}", toBase16(overloads.Parent), overloads.Name)); - res.set("kind", "overloads"); - res.set("name", overloads.Name); - res.set("members", dom::LazyArray(overloads.Members, domCorpus)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + namespace stdr = std::ranges; + namespace stdv = std::ranges::views; + auto newMembers = stdv::filter(Other.Members, [&](auto const& Member) { + return stdr::find(I.Members, Member) == I.Members.end(); + }); + I.Members.insert(I.Members.end(), newMembers.begin(), newMembers.end()); +} - // Copy other redundant fields from the first member - if (overloads.Members.size() > 0) +void +addMember(OverloadsInfo& I, FunctionInfo const& Member) +{ + if (I.Members.empty()) { - dom::Value const member = domCorpus->get(overloads.Members[0]); - if (member.isObject()) - { - for (std::string_view const key: - { "parent", - "parents", - "access", - "extraction", - "isRegular", - "isSeeBelow", - "isImplementationDefined", - "isDependency" }) - { - res.set(key, member.get(key)); - } - } + I.Name = Member.Name; + I.Access = Member.Access; + I.Extraction = Member.Extraction; + I.Class = Member.Class; + I.OverloadedOperator = Member.OverloadedOperator; } - - // Copy relevant values from the first member with documentation - // that contains it. - for (std::string_view const key: {"doc", "loc", "dcl"}) + else { - for (std::size_t i = 0; i < overloads.Members.size(); ++i) - { - dom::Value member = domCorpus->get(overloads.Members[i]); - if (member.isObject() && member.getObject().exists(key)) - { - res.set(key, member.get(key)); - break; - } - } + I.Extraction = leastSpecific(I.Extraction, Member.Extraction); } - v = res; + merge(dynamic_cast(I), dynamic_cast(Member)); + I.Members.push_back(Member.id); } } // clang::mrdocs diff --git a/src/lib/Metadata/Info/Record.cpp b/src/lib/Metadata/Info/Record.cpp index e822502907..1b73ba849c 100644 --- a/src/lib/Metadata/Info/Record.cpp +++ b/src/lib/Metadata/Info/Record.cpp @@ -8,11 +8,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include +#include #include -#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { dom::String toString( @@ -31,6 +31,69 @@ toString( } } +namespace { +void +reduceSymbolIDs( + std::vector& list, + std::vector&& otherList) +{ + for(auto const& id : otherList) + { + if (auto it = llvm::find(list, id); it != list.end()) + { + continue; + } + list.push_back(id); + } +} +} // (anon) + + +void +merge(RecordTranche& I, RecordTranche&& Other) +{ + reduceSymbolIDs(I.NamespaceAliases, std::move(Other.NamespaceAliases)); + reduceSymbolIDs(I.Typedefs, std::move(Other.Typedefs)); + reduceSymbolIDs(I.Records, std::move(Other.Records)); + reduceSymbolIDs(I.Enums, std::move(Other.Enums)); + reduceSymbolIDs(I.Functions, std::move(Other.Functions)); + reduceSymbolIDs(I.StaticFunctions, std::move(Other.StaticFunctions)); + reduceSymbolIDs(I.Variables, std::move(Other.Variables)); + reduceSymbolIDs(I.StaticVariables, std::move(Other.StaticVariables)); + reduceSymbolIDs(I.Concepts, std::move(Other.Concepts)); + reduceSymbolIDs(I.Guides, std::move(Other.Guides)); + reduceSymbolIDs(I.Friends, std::move(Other.Friends)); +} + +void +merge(RecordInterface& I, RecordInterface&& Other) +{ + merge(I.Public, std::move(Other.Public)); + merge(I.Protected, std::move(Other.Protected)); + merge(I.Private, std::move(Other.Private)); +} + +void +merge(RecordInfo& I, RecordInfo&& Other) +{ + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (Other.KeyKind != RecordKeyKind::Struct && + I.KeyKind != Other.KeyKind) + { + I.KeyKind = Other.KeyKind; + } + I.IsTypeDef |= Other.IsTypeDef; + I.IsFinal |= Other.IsFinal; + I.IsFinalDestructor |= Other.IsFinalDestructor; + if (I.Bases.empty()) + { + I.Bases = std::move(Other.Bases); + } + merge(I.Interface, std::move(Other.Interface)); + if (! I.Template) + I.Template = std::move(Other.Template); +} + template void tag_invoke( @@ -54,5 +117,4 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Info/Scope.cpp b/src/lib/Metadata/Info/Scope.cpp deleted file mode 100644 index a68144026f..0000000000 --- a/src/lib/Metadata/Info/Scope.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include -#include -#include -#include - -namespace clang { -namespace mrdocs { - -dom::Array -generateScopeOverloadsArray( - ScopeInfo const& I, - DomCorpus const& domCorpus) -{ - /* Unfortunately, this information is not readily - available in the Corpus, so we can't have lazy - references to these members like we do for - other Info types. - */ - dom::Array res; - domCorpus.getCorpus().traverseOverloads(I, [&](const auto& C) - { - using BareT = std::remove_cvref_t; - static_assert(std::is_base_of_v || std::is_same_v); - if constexpr(std::is_base_of_v) - { - res.push_back(domCorpus.get(C.id)); - } - else /* if constexpr(std::is_same_v) */ - { - res.push_back(domCorpus.construct(C)); - } - }); - return res; -} - - -void -addMember( - ScopeInfo& P, - Info& C) -{ - // Include C.id in P.Members if it's not already there - if (bool const exists = std::ranges::find(P.Members, C.id) - != P.Members.end(); - !exists) - { - P.Members.emplace_back(C.id); - } - - // Include C.id in P.Lookups[C.Name] if it's not already there - auto& lookups = P.Lookups.try_emplace(C.Name).first->second; - if (bool const exists = std::ranges::find(lookups, C.id) != lookups.end(); - !exists) - { - lookups.emplace_back(C.id); - } -} - -} // mrdocs -} // clang diff --git a/src/lib/Metadata/Info/Specialization.cpp b/src/lib/Metadata/Info/Specialization.cpp new file mode 100644 index 0000000000..b534c27b07 --- /dev/null +++ b/src/lib/Metadata/Info/Specialization.cpp @@ -0,0 +1,30 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(SpecializationInfo& I, SpecializationInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(! I.Primary) + I.Primary = Other.Primary; + if(I.Args.empty()) + I.Args = std::move(Other.Args); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Typedef.cpp b/src/lib/Metadata/Info/Typedef.cpp new file mode 100644 index 0000000000..34229c3bd8 --- /dev/null +++ b/src/lib/Metadata/Info/Typedef.cpp @@ -0,0 +1,32 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include + +namespace clang::mrdocs { + +void merge(TypedefInfo& I, TypedefInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if (!I.IsUsing) + I.IsUsing = Other.IsUsing; + if (! I.Type) + I.Type = std::move(Other.Type); + if(! I.Template) + I.Template = std::move(Other.Template); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Using.cpp b/src/lib/Metadata/Info/Using.cpp new file mode 100644 index 0000000000..d60f3fe9eb --- /dev/null +++ b/src/lib/Metadata/Info/Using.cpp @@ -0,0 +1,50 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +namespace { + +void +reduceSymbolIDs( + std::vector& list, + std::vector&& otherList) +{ + for(auto const& id : otherList) + { + if (auto it = llvm::find(list, id); it != list.end()) + { + continue; + } + list.push_back(id); + } +} + +} // (anon) + +void +merge(UsingInfo& I, UsingInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + reduceSymbolIDs(I.UsingSymbols, std::move(Other.UsingSymbols)); + if (I.Class == UsingClass::Normal) + I.Class = Other.Class; + if (! I.Qualifier) + I.Qualifier = std::move(Other.Qualifier); +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/Info/Variable.cpp b/src/lib/Metadata/Info/Variable.cpp new file mode 100644 index 0000000000..9aac20b31a --- /dev/null +++ b/src/lib/Metadata/Info/Variable.cpp @@ -0,0 +1,47 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include + +namespace clang::mrdocs { + +void +merge(VariableInfo& I, VariableInfo&& Other) +{ + MRDOCS_ASSERT(canMerge(I, Other)); + merge(dynamic_cast(I), std::move(dynamic_cast(Other))); + if(! I.Type) + I.Type = std::move(Other.Type); + if(! I.Template) + I.Template = std::move(Other.Template); + if(I.Initializer.Written.empty()) + I.Initializer = std::move(Other.Initializer); + I.IsConstinit |= Other.IsConstinit; + I.IsThreadLocal |= Other.IsThreadLocal; + I.IsConstexpr |= Other.IsConstexpr; + I.IsInline |= Other.IsInline; + if (I.StorageClass == StorageClassKind::None) + { + I.StorageClass = Other.StorageClass; + } + for (auto& otherAttr: Other.Attributes) + { + if (std::ranges::find(I.Attributes, otherAttr) == I.Attributes.end()) + { + I.Attributes.push_back(otherAttr); + } + } +} + +} // clang::mrdocs + diff --git a/src/lib/Metadata/InfoNodes.json b/src/lib/Metadata/InfoNodes.json index 79b889e490..96ed32905f 100644 --- a/src/lib/Metadata/InfoNodes.json +++ b/src/lib/Metadata/InfoNodes.json @@ -11,6 +11,10 @@ "name": "function", "brief": "The symbol is a function" }, + { + "name": "overloads", + "brief": "The symbol is a set of function overloads" + }, { "name": "enum", "brief": "The symbol is an enum" diff --git a/src/lib/Metadata/Interface.cpp b/src/lib/Metadata/Interface.cpp deleted file mode 100644 index c442f5ad73..0000000000 --- a/src/lib/Metadata/Interface.cpp +++ /dev/null @@ -1,554 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "lib/Dom/LazyArray.hpp" -#include "lib/Lib/ConfigImpl.hpp" -#include "lib/Support/Debug.hpp" -#include "mrdocs/Support/ScopeExit.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace clang { -namespace mrdocs { - -namespace { - -class TrancheBuilder -{ - const Corpus& corpus_; - const Info& parent_; - Tranche* none_; - Tranche* public_; - Tranche* protected_; - Tranche* private_; - - bool includePrivate_ = true; - bool buildingFromBase_ = false; - - Tranche* trancheFor(AccessKind access) - { - switch(access) - { - case AccessKind::Public: - return public_; - case AccessKind::Protected: - return protected_; - case AccessKind::Private: - return private_; - case AccessKind::None: - return none_; - default: - MRDOCS_UNREACHABLE(); - } - } - - void - push( - ScopeInfo& S, - const Info& I) - { - if(std::ranges::find(S.Members, I.id) == S.Members.end()) - S.Members.emplace_back(I.id); - auto& lookups = S.Lookups.try_emplace(I.Name).first->second; - if(std::ranges::find(lookups, I.id) == lookups.end()) - lookups.emplace_back(I.id); - } - - void - push( - std::vector& M, - const Info& I) - { - if(std::ranges::find(M, I.id) == M.end()) - M.emplace_back(I.id); - } - - template - void - push( - ListTy Tranche::* list, - AccessKind access, - Info const& member) - { - if (Tranche* tranche = trancheFor(access)) - { - push(tranche->*list, member); - } - } - - void - push( - std::vector& list, - AccessKind access, - FunctionInfo const& member) - { - // When adding a function to the tranche, we have to - // check if the function isn't already overriden by - // a function with the same name and signature. - if (trancheFor(access)) - { - if (buildingFromBase_) - { - // Iterate members of list - for (auto const& id: list) - { - // Get the function info - auto const& I = corpus_.get(id); - // Check if the function is overriden or shadowed - // TODO: We need to improve the function signature comparison - if (I.Name == member.Name) - { - // The function is shadowed, we don't add it - return; - } - } - } - push(list, member); - } - } - - static - AccessKind - effectiveAccess( - AccessKind memberAccess, - AccessKind baseAccess) noexcept - { - if (memberAccess == AccessKind::None || - baseAccess == AccessKind::None) - { - return AccessKind::None; - } - if (memberAccess == AccessKind::Private || - baseAccess == AccessKind::Private) - { - return AccessKind::Private; - } - if (memberAccess == AccessKind::Protected || - baseAccess == AccessKind::Protected) - { - return AccessKind::Protected; - } - return AccessKind::Public; - } - - bool - isFromParent(const Info& I) - { - return - I.Parent && - I.Parent == parent_.id; - } - - const Info* - lookThroughTypedefs(const Info* I) - { - if (!I || - !I->isTypedef()) - { - return I; - } - auto* TI = static_cast(I); - return lookThroughTypedefs( - corpus_.find(TI->Type->namedSymbol())); - } - -public: - TrancheBuilder( - const Corpus& corpus, - const Info& Parent, - Tranche* None, - Tranche* Public, - Tranche* Protected, - Tranche* Private) - : corpus_(corpus) - , parent_(Parent) - , none_(None) - , public_(Public) - , protected_(Protected) - , private_(Private) - { - auto& config = static_cast< - ConfigImpl const&>(corpus_.config); - includePrivate_ = config->privateMembers; - } - - void - add( - SymbolID const& id, - AccessKind baseAccess) - { - const auto& I = corpus_.get(id); - auto actualAccess = effectiveAccess(I.Access, baseAccess); - visit(I, *this, actualAccess); - } - - // Add members from RecordInfo to the tranches - void - addFrom( - const RecordInfo& I, - AccessKind baseAccess) - { - for (auto const& id: I.Members) - { - add(id, baseAccess); - } - // Add members from the base classes to the tranches - ScopeExitRestore s(buildingFromBase_, true); - for (auto const& B : I.Bases) - { - auto const actualAccess = - effectiveAccess(baseAccess, B.Access); - - if (!includePrivate_ && - actualAccess == AccessKind::Private) - { - continue; - } - Info const* Base = lookThroughTypedefs( - corpus_.find(B.Type->namedSymbol())); - if (!Base || - Base->id == I.id || - !Base->isRecord()) - { - continue; - } - addFrom(*static_cast< - const RecordInfo*>(Base), actualAccess); - } - } - - // Add members from RecordInfo to the tranches - void - addFrom(const NamespaceInfo& I) - { - for (auto const& id: I.Members) - { - add(id, AccessKind::None); - } - } - - // Add a namespace to the tranche - void - operator()( - const NamespaceInfo& I, - AccessKind access) - { - push(&Tranche::Namespaces, access, I); - } - - // Add a record to the tranche - void - operator()( - const RecordInfo& I, - AccessKind access) - { - push(&Tranche::Records, access, I); - } - - void - operator()( - SpecializationInfo const&, - AccessKind) - { - // KRYSTIAN FIXME: currently unimplemented - } - - // Add a function to the tranche - void operator()( - const FunctionInfo& I, - AccessKind const access) - { - // do not inherit constructors or destructors - if (parent_.isRecord() && !isFromParent(I) - && (I.Class == FunctionClass::Constructor - || I.Class == FunctionClass::Destructor)) - { - return; - } - - bool const isStatic = I.StorageClass == StorageClassKind::Static; - if (!parent_.isRecord() || - !isStatic) - { - push(&Tranche::Functions, access, I); - push(&Tranche::Overloads, access, I); - } - else if(isStatic) - { - push(&Tranche::StaticFunctions, access, I); - push(&Tranche::StaticOverloads, access, I); - } - } - - void operator()( - const TypedefInfo& I, - AccessKind access) - { - push(&Tranche::Types, access, I); - } - - void operator()( - const EnumInfo& I, - AccessKind access) - { - push(&Tranche::Enums, access, I); - } - - void operator()( - const FieldInfo& I, - AccessKind access) - { - push(&Tranche::Fields, access, I); - } - - void operator()( - const VariableInfo& I, - AccessKind access) - { - push(&Tranche::Variables, access, I); - } - - void operator()( - const FriendInfo& I, - AccessKind access) - { - push(&Tranche::Friends, access, I); - } - - void operator()( - NamespaceAliasInfo const& I, - AccessKind access) - { - push(&Tranche::NamespaceAliases, access, I); - } - - void operator()( - UsingInfo const& I, - AccessKind access) - { - push(&Tranche::Usings, access, I); - } - - void operator()( - const EnumConstantInfo&, - AccessKind) - { - // KRYSTIAN FIXME: currently unimplemented - } - - void operator()( - const GuideInfo& I, - AccessKind access) - { - // declarations of deduction guides are not inherited - if(parent_.isRecord() && ! isFromParent(I)) - return; - push(&Tranche::Guides, access, I); - } - - void operator()( - ConceptInfo const& I, - AccessKind access) - { - push(&Tranche::Concepts, access, I); - } -}; - -void -buildTranches( - const Corpus& corpus, - const Info& I, - Tranche* None, - Tranche* Public, - Tranche* Protected, - Tranche* Private) -{ - TrancheBuilder builder( - corpus, - I, - None, - Public, - Protected, - Private); - visit(I, [&](const InfoTy& II) - { - if constexpr (InfoTy::isRecord()) - { - builder.addFrom(II, AccessKind::Public); - } - if constexpr (InfoTy::isNamespace()) - { - builder.addFrom(II); - } - }); -} - -} // (anon) - -Interface:: -Interface( - Corpus const& corpus_) noexcept - : corpus(corpus_) -{ - Public = std::make_shared(); - Protected = std::make_shared(); - Private = std::make_shared(); -} - -Interface -makeInterface( - RecordInfo const& Derived, - Corpus const& corpus) -{ - Interface I(corpus); - buildTranches(corpus, Derived, nullptr, - I.Public.get(), I.Protected.get(), I.Private.get()); - return I; -} - -Tranche -makeTranche( - NamespaceInfo const& Namespace, - Corpus const& corpus) -{ - Tranche T; - buildTranches(corpus, Namespace, &T, - nullptr, nullptr, nullptr); - return T; -} - -/* A DOM object that represents a tranche - - This function creates an Interface object for a given - record. The Interface object is used to generate the - "interface" value of the DOM for symbols that represent - records or namespaces. - - The interface is not part of the Corpus. It is a temporary - structure generated to aggregate the symbols of a record. - This structure is provided to the user via the DOM. - */ -class DomTranche : public dom::DefaultObjectImpl -{ - std::shared_ptr tranche_; - - static - dom::Value - init( - std::span list, - DomCorpus const& domCorpus) - { - return dom::LazyArray(list, [&](SymbolID const& id) - { - return domCorpus.get(id); - }); - } - - static - dom::Value - init( - const ScopeInfo& scope, - DomCorpus const& domCorpus) - { - return generateScopeOverloadsArray(scope, domCorpus); - } - - -public: - DomTranche( - std::shared_ptr const& tranche, - DomCorpus const& domCorpus) noexcept - : dom::DefaultObjectImpl({ - #define INFO(Plural, LC_Plural) \ - { #LC_Plural, init(tranche->Plural, domCorpus) }, - #include - { "types", init(tranche->Types, domCorpus) }, - { "staticfuncs", init(tranche->StaticFunctions, domCorpus) }, - { "overloads", init(tranche->Overloads, domCorpus) }, - { "staticoverloads", init(tranche->StaticOverloads, domCorpus) }, - }) - , tranche_(tranche) - { - } -}; - -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - std::shared_ptr const& sp, - DomCorpus const* domCorpus) -{ - /* Unfortunately, we cannot use LazyObject like we do - in DomCorpus because the Tranche object is not - part of the Corpus. - - We must create a new object and eagerly populate it - with the values from the Tranche object. - - Unfortunately, we cannot use the default - dom::Object type either. - - We also need a custom object impl type because we - need to store a shared_ptr to the Tranche object - to keep it alive. - */ - if (!sp) - { - v = nullptr; - return; - } - v = dom::newObject(sp, *domCorpus); -} - -void -tag_invoke( - dom::ValueFromTag, - dom::Value& v, - std::shared_ptr const& sp, - DomCorpus const* domCorpus) -{ - /* Unfortunately, we cannot use LazyObject like we do - in DomCorpus because the Interface object is not - part of the Corpus. - - We must create a new object and eagerly populate it - with the values from the individual Tranche objects. - */ - if (!sp) - { - v = nullptr; - return; - } - // The dom::Value for each tranche will keep the - // respective shared_ptr alive. - v = dom::Object({ - { "public", dom::ValueFrom(sp->Public, domCorpus) }, - { "protected", dom::ValueFrom(sp->Protected, domCorpus) }, - { "private", dom::ValueFrom(sp->Private, domCorpus) } - }); -} - - -} // mrdocs -} // clang diff --git a/src/lib/Metadata/Javadoc.cpp b/src/lib/Metadata/Javadoc.cpp index b15168429f..cdd5a294d9 100644 --- a/src/lib/Metadata/Javadoc.cpp +++ b/src/lib/Metadata/Javadoc.cpp @@ -182,7 +182,7 @@ Javadoc:: operator==(Javadoc const& other) const noexcept { return std::ranges::equal(blocks_, other.blocks_, - [](const auto& a, const auto& b) + [](auto const& a, auto const& b) { return a->equals(static_cast(*b)); }); diff --git a/src/lib/Metadata/Name.cpp b/src/lib/Metadata/Name.cpp index aab1cb8dcb..17e3bad452 100644 --- a/src/lib/Metadata/Name.cpp +++ b/src/lib/Metadata/Name.cpp @@ -9,11 +9,11 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include -#include -#include -#include #include +#include +#include +#include +#include namespace clang { namespace mrdocs { @@ -44,9 +44,9 @@ std::strong_ordering NameInfo:: operator<=>(NameInfo const& other) const { - auto const r = std::tie(Kind, id, Name) <=> - std::tie(other.Kind, other.id, other.Name); - if (!std::is_eq(r)) + auto const r = std::tie(Kind, id, Name, Prefix) <=> + std::tie(other.Kind, other.id, other.Name, other.Prefix); + if (!std::is_eq(r) || Kind == NameKind::Identifier) { return r; } @@ -57,7 +57,7 @@ static void toStringImpl( std::string& result, - const NameInfo& N) + NameInfo const& N) { if (N.Prefix) { @@ -77,7 +77,7 @@ toStringImpl( if(! targs.empty()) { auto targ_writer = - [&](const U& u) + [&](U const& u) { if constexpr(U::isType()) { @@ -96,7 +96,7 @@ toStringImpl( writeTo(result, "..."); }; visit(*targs.front(), targ_writer); - for(const auto& arg : targs.subspan(1)) + for(auto const& arg : targs.subspan(1)) { writeTo(result, ", "); visit(*arg, targ_writer); diff --git a/src/lib/Metadata/Reduce.cpp b/src/lib/Metadata/Reduce.cpp deleted file mode 100644 index 877a413dce..0000000000 --- a/src/lib/Metadata/Reduce.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "Reduce.hpp" -#include -#include -#include - -namespace clang { -namespace mrdocs { - -namespace { - -struct LocationEqual -{ - bool - operator()( - Location const& L0, - Location const& L1) const noexcept - { - return - std::tie(L0.LineNumber, L0.FullPath) == - std::tie(L1.LineNumber, L1.FullPath); - } -}; - -struct LocationLess -{ - // This operator is used to sort a vector of Locations. - // No specific order (attributes more important than others) is required. Any - // sort is enough, the order is only needed to call std::unique after sorting - // the vector. - bool operator()( - Location const& L0, - Location const& L1) const noexcept - { - return - std::tie(L0.LineNumber, L0.FullPath) < - std::tie(L1.LineNumber, L1.FullPath); - } -}; - -void -reduceSymbolIDs( - std::vector& list, - std::vector&& otherList) -{ - for(auto const& id : otherList) - { - auto it = llvm::find(list, id); - if(it != list.end()) - continue; - list.push_back(id); - } -} - -void -reduceLookups( - std::unordered_map>& I, - std::unordered_map>&& Other) -{ - I.merge(std::move(Other)); - for(auto& [name, ids] : Other) - { - auto [it, created] = I.try_emplace(name); - reduceSymbolIDs(it->second, std::move(ids)); - } -} - -} // (anon) - -#ifndef NDEBUG -static bool canMerge(Info const& I, Info const& Other) -{ - return - I.Kind == Other.Kind && - I.id == Other.id; -} -#endif - -static void merge(Javadoc& I, Javadoc&& other) -{ - // FIXME: this doesn't merge parameter information; - // parameters with the same name but different direction - // or descriptions end up being duplicated - if(other != I) - { - // Unconditionally extend the blocks - // since each decl may have a comment. - I.append(std::move(other)); - } -} - -void -mergeInfo(Info& I, Info&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - MRDOCS_ASSERT(I.id); - if (I.Name == "") - { - I.Name = Other.Name; - } - if (I.Parent) - { - I.Parent = std::move(Other.Parent); - } - if (I.Access == AccessKind::None) - { - I.Access = Other.Access; - } - I.Extraction = leastSpecific(I.Extraction, Other.Extraction); - - // Append javadocs - if (!I.javadoc) - { - I.javadoc = std::move(Other.javadoc); - } - else if (Other.javadoc) - { - merge(*I.javadoc, std::move(*Other.javadoc)); - } -} - -void mergeScopeInfo(ScopeInfo& I, ScopeInfo&& Other) -{ - reduceSymbolIDs(I.Members, std::move(Other.Members)); - reduceLookups(I.Lookups, std::move(Other.Lookups)); -} - -static void mergeSourceInfo( - SourceInfo& I, - SourceInfo&& Other) -{ - if (! I.DefLoc) - I.DefLoc = std::move(Other.DefLoc); - // Unconditionally extend the list of locations, since we want all of them. - std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(I.Loc)); - // VFALCO This has the fortuituous effect of also canonicalizing - llvm::sort(I.Loc, LocationLess{}); - auto Last = std::unique(I.Loc.begin(), I.Loc.end(), LocationEqual{}); - I.Loc.erase(Last, I.Loc.end()); -} - -static void mergeExprInfo( - ExprInfo& I, - ExprInfo&& Other) -{ - if(I.Written.empty()) - I.Written = std::move(Other.Written); -} - -template -static void mergeExprInfo( - ConstantExprInfo& I, - ConstantExprInfo&& Other) -{ - mergeExprInfo( - static_cast(I), - static_cast(Other)); - if(! I.Value) - I.Value = std::move(Other.Value); -} - -void merge(NamespaceInfo& I, NamespaceInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - mergeScopeInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - reduceSymbolIDs(I.UsingDirectives, - std::move(Other.UsingDirectives)); - - I.IsInline |= Other.IsInline; - I.IsAnonymous |= Other.IsAnonymous; -} - -void merge(RecordInfo& I, RecordInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(Other.KeyKind != RecordKeyKind::Struct && - I.KeyKind != Other.KeyKind) - I.KeyKind = Other.KeyKind; - I.IsTypeDef |= Other.IsTypeDef; - I.IsFinal |= Other.IsFinal; - I.IsFinalDestructor |= Other.IsFinalDestructor; - - if (I.Bases.empty()) - I.Bases = std::move(Other.Bases); - // KRYSTIAN FIXME: really should use explicit cases here. - // at a glance, it is not obvious that we are binding to - // the SymboInfo base class subobject - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - mergeScopeInfo(I, std::move(Other)); - if (! I.Template) - I.Template = std::move(Other.Template); -} - -void merge(FunctionInfo& I, FunctionInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(I.Class == FunctionClass::Normal) - I.Class = Other.Class; - if (! I.ReturnType) - I.ReturnType = std::move(Other.ReturnType); - if (I.Params.empty()) - I.Params = std::move(Other.Params); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - if (!I.Template) - I.Template = std::move(Other.Template); - if(I.Noexcept.Implicit) - I.Noexcept = std::move(Other.Noexcept); - if(I.Explicit.Implicit) - I.Explicit = std::move(Other.Explicit); - mergeExprInfo(I.Requires, std::move(Other.Requires)); - - I.IsVariadic |= Other.IsVariadic; - I.IsVirtual |= Other.IsVirtual; - I.IsVirtualAsWritten |= Other.IsVirtualAsWritten; - I.IsPure |= Other.IsPure; - I.IsDefaulted |= Other.IsDefaulted; - I.IsExplicitlyDefaulted |= Other.IsExplicitlyDefaulted; - I.IsDeleted |= Other.IsDeleted; - I.IsDeletedAsWritten |= Other.IsDeletedAsWritten; - I.IsNoReturn |= Other.IsNoReturn; - I.HasOverrideAttr |= Other.HasOverrideAttr; - I.HasTrailingReturn |= Other.HasTrailingReturn; - I.IsConst |= Other.IsConst; - I.IsVolatile |= Other.IsVolatile; - I.IsFinal |= Other.IsFinal; - I.IsNodiscard |= Other.IsNodiscard; - I.IsExplicitObjectMemberFunction |= Other.IsExplicitObjectMemberFunction; - - if(I.Constexpr == ConstexprKind::None) - I.Constexpr = Other.Constexpr; - if(I.StorageClass == StorageClassKind::None) - I.StorageClass = Other.StorageClass; - if(I.RefQualifier == ReferenceKind::None) - I.RefQualifier = Other.RefQualifier; - if(I.OverloadedOperator == OperatorKind::None) - I.OverloadedOperator = Other.OverloadedOperator; -} - -void merge(GuideInfo& I, GuideInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - - if (! I.Deduced) - I.Deduced = std::move(Other.Deduced); - if (I.Params.empty()) - I.Params = std::move(Other.Params); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - if (!I.Template) - I.Template = std::move(Other.Template); - if(I.Explicit.Implicit) - I.Explicit = std::move(Other.Explicit); -} - -void merge(TypedefInfo& I, TypedefInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if (!I.IsUsing) - I.IsUsing = Other.IsUsing; - if (! I.Type) - I.Type = std::move(Other.Type); - if(! I.Template) - I.Template = std::move(Other.Template); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); -} - -void merge(EnumInfo& I, EnumInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(! I.Scoped) - I.Scoped = Other.Scoped; - if (! I.UnderlyingType) - I.UnderlyingType = std::move(Other.UnderlyingType); - mergeScopeInfo(I, std::move(Other)); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); -} - -void merge(FieldInfo& I, FieldInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(! I.Type) - I.Type = std::move(Other.Type); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - if(I.Default.Written.empty()) - I.Default = std::move(Other.Default); - - I.IsBitfield |= Other.IsBitfield; - mergeExprInfo(I.BitfieldWidth, - std::move(Other.BitfieldWidth)); - - I.IsVariant |= Other.IsVariant; - I.IsMutable |= Other.IsMutable; - I.IsMaybeUnused |= Other.IsMaybeUnused; - I.IsDeprecated |= Other.IsDeprecated; - I.HasNoUniqueAddress |= Other.HasNoUniqueAddress; -} - -void merge(VariableInfo& I, VariableInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(! I.Type) - I.Type = std::move(Other.Type); - if(! I.Template) - I.Template = std::move(Other.Template); - if(I.Initializer.Written.empty()) - I.Initializer = std::move(Other.Initializer); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - - I.IsConstinit |= Other.IsConstinit; - I.IsThreadLocal |= Other.IsThreadLocal; - I.IsConstexpr |= Other.IsConstexpr; - I.IsInline |= Other.IsInline; - if (I.StorageClass == StorageClassKind::None) - { - I.StorageClass = Other.StorageClass; - } - for (auto& otherAttr: Other.Attributes) - { - if (std::ranges::find(I.Attributes, otherAttr) == I.Attributes.end()) - { - I.Attributes.push_back(otherAttr); - } - } -} - -void merge(SpecializationInfo& I, SpecializationInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(! I.Primary) - I.Primary = Other.Primary; - if(I.Args.empty()) - I.Args = std::move(Other.Args); - mergeInfo(I, std::move(Other)); - mergeScopeInfo(I, std::move(Other)); -} - -void merge(FriendInfo& I, FriendInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - if(! I.FriendSymbol) - I.FriendSymbol = Other.FriendSymbol; - if(! I.FriendType) - I.FriendType = std::move(Other.FriendType); -} - -void merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if (! I.AliasedSymbol) - I.AliasedSymbol = std::move(Other.AliasedSymbol); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); -} - -void merge(UsingInfo& I, UsingInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - - reduceSymbolIDs(I.UsingSymbols, std::move(Other.UsingSymbols)); - if (I.Class == UsingClass::Normal) - I.Class = Other.Class; - if (! I.Qualifier) - I.Qualifier = std::move(Other.Qualifier); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); -} - -void merge(EnumConstantInfo& I, EnumConstantInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(I.Initializer.Written.empty()) - I.Initializer = std::move(Other.Initializer); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); -} - -void merge(ConceptInfo& I, ConceptInfo&& Other) -{ - MRDOCS_ASSERT(canMerge(I, Other)); - if(I.Constraint.Written.empty()) - I.Constraint = std::move(Other.Constraint); - mergeSourceInfo(I, std::move(Other)); - mergeInfo(I, std::move(Other)); - if (! I.Template) - I.Template = std::move(Other.Template); -} - -} // mrdocs -} // clang diff --git a/src/lib/Metadata/Reduce.hpp b/src/lib/Metadata/Reduce.hpp index e946f6058b..ec011697c6 100644 --- a/src/lib/Metadata/Reduce.hpp +++ b/src/lib/Metadata/Reduce.hpp @@ -12,29 +12,11 @@ #ifndef MRDOCS_LIB_METADATA_REDUCE_HPP #define MRDOCS_LIB_METADATA_REDUCE_HPP -#include -#include -#include +#include #include #include -namespace clang { -namespace mrdocs { - -void merge(NamespaceInfo& I, NamespaceInfo&& Other); -void merge(RecordInfo& I, RecordInfo&& Other); -void merge(FunctionInfo& I, FunctionInfo&& Other); -void merge(TypedefInfo& I, TypedefInfo&& Other); -void merge(EnumInfo& I, EnumInfo&& Other); -void merge(FieldInfo& I, FieldInfo&& Other); -void merge(VariableInfo& I, VariableInfo&& Other); -void merge(SpecializationInfo& I, SpecializationInfo&& Other); -void merge(FriendInfo& I, FriendInfo&& Other); -void merge(EnumConstantInfo& I, EnumConstantInfo&& Other); -void merge(GuideInfo& I, GuideInfo&& Other); -void merge(NamespaceAliasInfo& I, NamespaceAliasInfo&& Other); -void merge(UsingInfo& I, UsingInfo&& Other); -void merge(ConceptInfo& I, ConceptInfo&& Other); +namespace clang::mrdocs { // // This file defines the merging of different types of infos. The data in the @@ -52,14 +34,15 @@ void merge(ConceptInfo& I, ConceptInfo&& Other); template std::unique_ptr -reduce( - std::vector>& Values) +reduce(std::vector>& Values) { MRDOCS_ASSERT(! Values.empty() && Values[0]); std::unique_ptr Merged = std::make_unique(Values[0]->id); T* Tmp = static_cast(Merged.get()); - for (auto& I : Values) + for (auto& I: Values) + { merge(*Tmp, std::move(*static_cast(I.get()))); + } return Merged; } @@ -96,8 +79,7 @@ reduceChildren( } } -} // mrdocs -} // clang +} // clang::mrdocs #endif \ No newline at end of file diff --git a/src/lib/Metadata/Source.cpp b/src/lib/Metadata/Source.cpp index f3878036f3..5ceff021fa 100644 --- a/src/lib/Metadata/Source.cpp +++ b/src/lib/Metadata/Source.cpp @@ -9,12 +9,13 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include +#include +#include +#include #include -#include "lib/Dom/LazyObject.hpp" -#include "lib/Dom/LazyArray.hpp" -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { std::string_view toString(FileKind kind) @@ -32,6 +33,53 @@ toString(FileKind kind) }; } +namespace +{ +template +void +mergeImpl(SourceInfo& I, SourceInfoTy&& Other) +{ + if (!I.DefLoc) + { + I.DefLoc = Other.DefLoc; + } + else if (Other.DefLoc) + { + if (!I.DefLoc->Documented && Other.DefLoc->Documented) + { + I.DefLoc = Other.DefLoc; + } + else + { + I.DefLoc = std::min(I.DefLoc, Other.DefLoc); + } + } + if constexpr (Move) + { + std::ranges::move(Other.Loc, std::back_inserter(I.Loc)); + } + else + { + std::ranges::copy(Other.Loc, std::back_inserter(I.Loc)); + } + std::ranges::sort(I.Loc); + auto const Last = std::ranges::unique(I.Loc).begin(); + I.Loc.erase(Last, I.Loc.end()); +} +} + +void +merge(SourceInfo& I, SourceInfo const& Other) +{ + mergeImpl(I, Other); +} + +void +merge(SourceInfo& I, SourceInfo&& Other) +{ + mergeImpl(I, Other); +} + template void tag_invoke( @@ -81,5 +129,4 @@ tag_invoke( v = dom::LazyObject(I); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Specifiers.cpp b/src/lib/Metadata/Specifiers.cpp index f35df6a1ca..7bc54fafc9 100644 --- a/src/lib/Metadata/Specifiers.cpp +++ b/src/lib/Metadata/Specifiers.cpp @@ -78,7 +78,7 @@ dom::String toString(NoexceptKind kind) noexcept dom::String toString( - const NoexceptInfo& info, + NoexceptInfo const& info, bool resolved, bool implicit) { diff --git a/src/lib/Metadata/Symbols.cpp b/src/lib/Metadata/Symbols.cpp index a969d158c0..2cc68cb6e2 100644 --- a/src/lib/Metadata/Symbols.cpp +++ b/src/lib/Metadata/Symbols.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace clang { namespace mrdocs { @@ -23,6 +24,20 @@ namespace mrdocs { // we are going to be having some problems... static_assert(CHAR_BIT == 8); +SymbolID +SymbolID:: +createFromString(std::string_view const input) +{ + // Compute the SHA1 hash of the input string + llvm::SHA1 sha1; + sha1.update(input); + auto const result = sha1.final(); + static_assert(result.size() == 20); + SymbolID const id(reinterpret_cast(result.data())); + return id; +} + + constexpr unsigned char tolower(char c) noexcept @@ -33,6 +48,13 @@ tolower(char c) noexcept return uc; } +std::string +toBase16Str(SymbolID const& id) +{ + return toBase16(id); +} + + std::strong_ordering compareSymbolNames( std::string_view s0, diff --git a/src/lib/Metadata/Template.cpp b/src/lib/Metadata/Template.cpp index 5c845db373..7e0d271e01 100644 --- a/src/lib/Metadata/Template.cpp +++ b/src/lib/Metadata/Template.cpp @@ -10,18 +10,16 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include -#include +#include +#include #include -#include -#include +#include +#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { std::string_view -toString( - TArgKind kind) noexcept +toString(TArgKind kind) noexcept { switch(kind) { @@ -87,10 +85,10 @@ toString( std::string toString( - const TArg& arg) noexcept + TArg const& arg) noexcept { return visit(arg, - [](const T& t) + [](T const& t) { std::string result; if constexpr(T::isType()) @@ -123,7 +121,7 @@ tag_invoke( { io.map("kind", toString(I.Kind)); io.map("is-pack", I.IsPackExpansion); - visit(I, [&io](const T& t) { + visit(I, [&io](T const& t) { if constexpr(T::isType()) { io.map("type", t.Type); @@ -162,7 +160,7 @@ tag_invoke( io.map("kind", toString(I.Kind)); io.map("name", dom::stringOrNull(I.Name)); io.map("is-pack", I.IsParameterPack); - visit(I, [domCorpus, &io](const T& t) { + visit(I, [domCorpus, &io](T const& t) { if(t.Default) { io.map("default", t.Default); @@ -223,5 +221,4 @@ tag_invoke( v = dom::LazyObject(I, domCorpus); } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Type.cpp b/src/lib/Metadata/Type.cpp index 5aa8921246..35d4046200 100644 --- a/src/lib/Metadata/Type.cpp +++ b/src/lib/Metadata/Type.cpp @@ -8,10 +8,10 @@ // Official repository: https://github.com/cppalliance/mrdocs // +#include +#include #include #include -#include -#include namespace clang { namespace mrdocs { @@ -106,7 +106,7 @@ struct TypeBeforeWriter inline void operator()( - const T& t, + T const& t, auto& write, std::bool_constant) const; @@ -121,7 +121,7 @@ struct TypeAfterWriter inline void operator()( - const T& t, + T const& t, auto& write, std::bool_constant) const; @@ -130,7 +130,7 @@ struct TypeAfterWriter template void writeFullType( - const T& t, + T const& t, auto& write) { visit(t, writeTypeBefore, write, std::false_type{}); @@ -233,7 +233,7 @@ inline void TypeAfterWriter:: operator()( - const T& t, + T const& t, auto& write, std::bool_constant) const { diff --git a/src/lib/Support/Assert.cpp b/src/lib/Support/Assert.cpp index 4707947407..c8eb8698b1 100644 --- a/src/lib/Support/Assert.cpp +++ b/src/lib/Support/Assert.cpp @@ -22,8 +22,8 @@ namespace mrdocs { void assert_failed( - const char* msg, - const char* file, + char const* msg, + char const* file, std::uint_least32_t line) { llvm::errs() << fmt::format( diff --git a/src/lib/Support/GeneratorsImpl.cpp b/src/lib/Support/GeneratorsImpl.cpp index 536c528ab4..e1ac134e60 100644 --- a/src/lib/Support/GeneratorsImpl.cpp +++ b/src/lib/Support/GeneratorsImpl.cpp @@ -52,7 +52,7 @@ GeneratorsImpl:: find( std::string_view id) const noexcept { - for (const auto & li : list_) + for (auto const& li : list_) { if(li->id() == id) { diff --git a/src/lib/Support/LegibleNames.cpp b/src/lib/Support/LegibleNames.cpp index de8a08408d..2a29a4b89a 100644 --- a/src/lib/Support/LegibleNames.cpp +++ b/src/lib/Support/LegibleNames.cpp @@ -5,6 +5,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -15,6 +16,7 @@ #include "lib/Support/Debug.hpp" #include #include +#include #include #include #include @@ -23,343 +25,299 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { +namespace { + +std::string_view +getUnnamedInfoName(Info const& I) +{ + // All valid c++ identifiers begin with + // an underscore or alphabetic character, + // so a numeric prefix ensures no conflicts + if (I.isFunction() || I.isOverloads()) + { + static + constexpr + std::string_view + func_reserved[] = { + "2function", + "2constructor", + "2conversion", + "2destructor" + }; + std::size_t func_idx = 0; + if (auto const* FI = dynamic_cast(&I)) + { + // don't use the reserved prefix for overloaded operators + if(FI->Class == FunctionClass::Normal && + FI->OverloadedOperator != OperatorKind::None) + { + return getSafeOperatorName( + FI->OverloadedOperator, true); + } + func_idx = to_underlying(FI->Class); + } + if (auto const* FI = dynamic_cast(&I)) + { + // don't use the reserved prefix for overloaded operators + if(FI->Class == FunctionClass::Normal && + FI->OverloadedOperator != OperatorKind::None) + { + return getSafeOperatorName( + FI->OverloadedOperator, true); + } + func_idx = to_underlying(FI->Class); + } + MRDOCS_ASSERT(func_idx < std::size(func_reserved)); + return func_reserved[func_idx]; + } + + static + constexpr + std::string_view + reserved[] = { + "00namespace", + "01record", + "02function", + "03overloads", + "04enum", + "05enum-constant", + "06typedef", + "07variable", + "08field", + "09specialization", + "08friend", + "12guide", + "12namespace-alias", + "13using", + "14concept" + }; + std::size_t const idx = to_underlying(I.Kind) - 1; + MRDOCS_ASSERT(idx < std::size(reserved)); + return reserved[idx]; +} +} // namespace class LegibleNames::Impl { Corpus const& corpus_; - // name used for the global namespace + /* Name used for the global namespace + + This is typically "index" or "global" + if a symbol has the same name as the + global namespace, then it needs to be + disambiguated + */ std::string global_ns_; - // store all info required to generate a legible name + /* The info related to the legible name for a symbol + */ struct LegibleNameInfo { - // legible name without disambiguation characters + /* Raw unqualified name for the symbol + */ std::string_view unqualified; - // number of characters from the SymbolID string - // required to uniquely identify this symbol + + /* Number of characters from the SymbolID string + required to uniquely identify this symbol + */ std::uint8_t disambig_chars; - // SymbolID converted to a string + + /* SymbolID converted to a string + */ std::string id_str; }; + /* A map from SymbolID to legible name information + */ std::unordered_map map_; - // maps unqualified names to all symbols - // with that name within the current scope - std::unordered_multimap disambiguation_map_; + /* Maps unqualified names to all symbols with that name within the current scope + */ + std::unordered_multimap disambiguation_map_; - std::string_view - getReserved(const Info& I) +public: + /* Build the map of legible names for all symbols in the corpus + */ + Impl(Corpus const& corpus, std::string_view const global_ns) + : corpus_(corpus) + , global_ns_(global_ns) { - // all valid c++ identifiers begin with - // an underscore or alphabetic character, - // so a numeric prefix ensures no conflicts - static constexpr - std::string_view - reserved[] = { - "00namespace", - "01record", - "02function", - "03enum", - "04typedef", - "05variable", - "06field", - "07specialization", - "09enum-constant", - "08friend", - "10guide", - "11namespace-alias", - "12using", - "13concept", - }; - if(I.isFunction()) + NamespaceInfo const& global = corpus_.globalNamespace(); + + // Treat the global namespace as-if its "name" + // is in the same scope as its members + buildLegibleMember(global, global_ns_); + visit(global, *this); + + // after generating legible names for every symbol, + // set the number of disambiguation characters + // used for the global namespace to zero + map_.at(global.id).disambig_chars = 0; + } + + /* Visit a symbol and build legible names for its members + */ + template + void + operator()(InfoTy const& I) + { + if constexpr (InfoParent && !std::same_as) { - static - std::string_view - func_reserved[] = { - "2function", - "2constructor", - "2conversion", - "2destructor" - }; - const auto& FI = static_cast< - const FunctionInfo&>(I); - // don't use the reserved prefix for overloaded operators - if(FI.Class == FunctionClass::Normal && - FI.OverloadedOperator != OperatorKind::None) - { - return getSafeOperatorName( - FI.OverloadedOperator, true); - } - std::size_t func_idx = to_underlying(FI.Class); - MRDOCS_ASSERT(func_idx < std::size(func_reserved)); - return func_reserved[func_idx]; + // Visit the members of the symbol and build legible names + constexpr Corpus::TraverseOptions opts = {.skipInherited = true}; + corpus_.traverse(opts, I, [this, &I](Info const& M) + { + auto const raw = getRawUnqualified(M); + buildLegibleMember(M, raw); + + // Traverse non inherited function overloads inline + if (M.isOverloads()) + { + auto const& O = static_cast(M); + corpus_.traverse(O, [this, &I](Info const& M2) + { + // Not inherited in regard to I + MRDOCS_CHECK_OR(M2.Parent == I.id); + auto const raw2 = getRawUnqualified(M2); + buildLegibleMember(M2, raw2); + }); + } + }); + + // Clear the disambiguation map for this scope + disambiguation_map_.clear(); + + // Visit the members of the symbol to build legible names + // for their members + corpus_.traverse(opts, I, [this](Info const& M) + { + visit(M, *this); + }); } - - std::size_t idx = to_underlying(I.Kind) - 1; - MRDOCS_ASSERT(idx < std::size(reserved)); - return reserved[idx]; } -public: + /* Get the raw unqualified name for a symbol + + This function returns a reference to the original symbol + name without any disambiguation characters. + + If the symbolhas no name, then a reserved name based + on the type is returned instead. + */ std::string_view - getUnqualified( - const SymbolID& id) + getRawUnqualified(SymbolID const& id) { - const Info* I = corpus_.find(id); + Info const* I = corpus_.find(id); MRDOCS_ASSERT(I); - return getUnqualified(*I); + return getRawUnqualified(*I); } + /* @copydoc getRawUnqualified(SymbolID const&) + */ std::string_view - getUnqualified( - const Info& I) + getRawUnqualified(Info const& I) { MRDOCS_ASSERT(I.id && I.id != SymbolID::global); - return visit(I, [&]( - const T& t) -> std::string_view + if (I.Name.empty()) + { + return getUnnamedInfoName(I); + } + + return visit(I, [&](T const& t) -> std::string_view { - // namespaces can be unnamed (i.e. anonymous) - if constexpr(T::isNamespace()) - { - if(t.IsAnonymous) - return getReserved(t); - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - // fields and typedefs cannot be overloaded - // or partially/explicitly specialized, - // but must have names + MRDOCS_ASSERT(!t.Name.empty()); if constexpr( - T::isField() || - T::isTypedef()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - // variables can be partially/explicitly - // specialized, but must have names and - // cannot be overloaded - if constexpr(T::isVariable()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - // enums cannot be overloaded or partially/ - // explicitly specialized, but can be unnamed - if constexpr(T::isEnum()) + T::isFunction() || + T::isOverloads()) { - /** KRYSTIAN FIXME: [dcl.enum] p12 states (paraphrased): - - an unnamed enumeration type that has a first enumerator - and does not have a typedef name for linkage purposes - is denoted by its underlying type and its - first enumerator for linkage purposes. - - should we also take this approach? note that this would not - address unnamed enumeration types without any enumerators. - */ - if(t.Name.empty()) - return getReserved(t); - return t.Name; + // functions can be explicitly specialized, + // and can be overloaded + if (t.Class != FunctionClass::Normal || + t.OverloadedOperator != OperatorKind::None) + { + return getUnnamedInfoName(t); + } } - - // records can be partially/explicitly specialized, - // and can be unnamed, but cannot be overloaded - if constexpr(T::isRecord()) + else if constexpr(T::isFriend()) { - if(t.Name.empty()) - return getReserved(t); - return t.Name; + return getUnnamedInfoName(t); } - - // functions must have named, - // can be explicitly specialized, - // and can be overloaded - if constexpr(T::isFunction()) - { - if(t.Class != FunctionClass::Normal || - t.OverloadedOperator != OperatorKind::None || - t.Name.empty()) - return getReserved(t); - MRDOCS_ASSERT(!t.Name.empty()); - return t.Name; - } - - if constexpr(T::isSpecialization()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - if constexpr(T::isFriend()) - { - return getReserved(t); - } - - if constexpr(T::isNamespaceAlias()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - if constexpr(T::isUsing()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - if constexpr(T::isEnumConstant()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - if constexpr(T::isGuide()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - if constexpr(T::isConcept()) - { - MRDOCS_ASSERT(! t.Name.empty()); - return t.Name; - } - - MRDOCS_UNREACHABLE(); + return t.Name; }); } - //-------------------------------------------- - - #define MINIMAL_SUFFIX - + /* Take the raw unqualified name for a symbol and build a legible name + */ void buildLegibleMember( - const Info& I, - std::string_view name) + Info const& I, + std::string_view rawName) { - // generate the unqualified name and SymbolID string - LegibleNameInfo& info = map_.emplace(I.id, LegibleNameInfo( - name, 0, toBase16(I.id, true))).first->second; - // if there are other symbols with the same name, then disambiguation - // is required. iterate over the other symbols with the same unqualified name, - // and calculate the minimum number of characters from the SymbolID needed - // to uniquely identify each symbol. then, update all symbols with the new value. - std::uint8_t max_required = 0; - auto [first, last] = disambiguation_map_.equal_range(name); - for(const auto& other : std::ranges::subrange( - first, last) | std::views::values) + // Generate the legible name information for this symbol + auto const idAsString = toBase16(I.id, true); + LegibleNameInfo LI(rawName, 0, idAsString); + LegibleNameInfo& info = map_.emplace(I.id, LI).first->second; + + // Look for symbols with the same unqualified name + auto [first, last] = disambiguation_map_.equal_range(rawName); + auto sameNameInfos = std::ranges::subrange(first, last) | std::views::values; + if (std::ranges::empty(sameNameInfos)) { - auto mismatch_it = std::ranges::mismatch( - info.id_str, other->id_str).in1; - std::uint8_t n_required = std::distance( - info.id_str.begin(), mismatch_it) + 1; - #ifndef MINIMAL_SUFFIX - max_required = std::max({max_required, - other->disambig_chars, n_required}); - #else - // update the suffix size for the other symbol - other->disambig_chars = std::max( - n_required, other->disambig_chars); - // update the suffix size needed for this symbol - max_required = std::max(max_required, n_required); - #endif + // Add this symbol to the disambiguation map + disambiguation_map_.emplace(rawName, &info); + // If there are no other symbols with the same name, then + // disambiguation is not required. + return; } - #ifndef MINIMAL_SUFFIX - if(max_required) - { - // update the number of disambiguation characters for each symbol - for(const auto& other : std::ranges::subrange( - first, last) | std::views::values) - other->disambig_chars = max_required; - info.disambig_chars = max_required; - } - #else - // use the longest suffix needed to disambiguate - // between all symbols with the same name in this scope - info.disambig_chars = max_required; - #endif - // add this symbol to the disambiguation map - disambiguation_map_.emplace(name, &info); - } - - //-------------------------------------------- - - template - void traverse(const InfoTy& I, Fn&& F) - { - if constexpr( - InfoTy::isSpecialization() || - InfoTy::isNamespace() || - InfoTy::isRecord() || - InfoTy::isEnum()) + // Iterate over the other symbols with the same raw unqualified name + // and update their legible name information + std::uint8_t suffix_size_required = 0; + for (LegibleNameInfo* other : sameNameInfos) { - for(const SymbolID& id : I.Members) - F(id); + // Find the first character that differs between the two symbol IDs + auto const mismatch_it = std::ranges::mismatch( + info.id_str, other->id_str).in1; + // Number of characters required to disambiguate + std::uint8_t n_required = std::distance(info.id_str.begin(), mismatch_it) + 1; + // Update the suffix size for the other symbol + other->disambig_chars = std::max(n_required, other->disambig_chars); + // Update the suffix size needed for this symbol + suffix_size_required = std::max(suffix_size_required, n_required); } - } - //-------------------------------------------- + // Use the longest suffix needed to disambiguate + // between all symbols with the same name in this scope + info.disambig_chars = suffix_size_required; -public: - Impl(Corpus const& corpus, std::string_view global_ns) - : corpus_(corpus) - , global_ns_(global_ns) - { - const NamespaceInfo& global = - corpus_.globalNamespace(); - // treat the global namespace as-if its "name" - // is in the same scope as its members - buildLegibleMember(global, global_ns_); - visit(global, *this); - // after generating legible names for every symbol, - // set the number of disambiguation characters - // used for the global namespace to zero - map_.at(global.id).disambig_chars = 0; - } - - template - void operator()(const InfoTy& I) - { - traverse(I, [this](const SymbolID& id) - { - if(const Info* M = corpus_.find(id)) - buildLegibleMember(*M, getUnqualified(*M)); - }); - // clear the disambiguation map after visiting the members, - // then build disambiguation information for each member - disambiguation_map_.clear(); - traverse(I, [this](const SymbolID& id) - { - if(const Info* M = corpus_.find(id)) - visit(*M, *this); - }); + // Add this symbol to the disambiguation map + disambiguation_map_.emplace(rawName, &info); } void getLegibleUnqualified( std::string& result, - const SymbolID& id) + SymbolID const& id) { MRDOCS_ASSERT(corpus_.exists(id)); + + // Find the legible name information for this symbol auto const it = map_.find(id); MRDOCS_ASSERT(it != map_.end()); auto& [unqualified, n_disambig, id_str] = it->second; + + // Append the unqualified name to the result + // The unqualified name has no reserved chars result.reserve( result.size() + unqualified.size() + n_disambig ? n_disambig + 2 : 0); result.append(unqualified); - if(n_disambig) + + // Append a disambiguation suffix from the symbol ID if needed + if (n_disambig) { // KRYSTIAN FIXME: the SymbolID chars must be prefixed with // a reserved character, otherwise there could be a @@ -373,18 +331,16 @@ class LegibleNames::Impl void getLegibleQualified( std::string& result, - const SymbolID& id, + SymbolID const& id, char const delim) { MRDOCS_ASSERT(corpus_.exists(id)); auto const& I = corpus_.get(id); - for(auto const& parent : getParents(corpus_, I)) + auto const curParent = I.Parent; + if (curParent != SymbolID::invalid && + curParent != SymbolID::global) { - if (!parent || parent == SymbolID::global) - { - continue; - } - getLegibleUnqualified(result, parent); + getLegibleQualified(result, curParent, delim); result.push_back(delim); } getLegibleUnqualified(result, id); @@ -396,10 +352,12 @@ class LegibleNames::Impl LegibleNames:: LegibleNames( Corpus const& corpus, - bool enabled) + bool const enabled) { - if(enabled) + if (enabled) + { impl_ = std::make_unique(corpus, "index"); + } } LegibleNames:: @@ -410,29 +368,20 @@ LegibleNames:: getUnqualified( SymbolID const& id) const { - if(! impl_) + if (!impl_) + { return toBase16(id); + } std::string result; impl_->getLegibleUnqualified(result, id); return result; } -std::string -LegibleNames:: -getUnqualified( - OverloadSet const& os) const -{ - std::string result = "overload-"; - // KRYSTIAN FIXME: the name needs to be hashed - result.append(os.Name); - return result; -} - std::string LegibleNames:: getQualified( SymbolID const& id, - char delim) const + char const delim) const { if (!impl_) { @@ -443,28 +392,4 @@ getQualified( return result; } -std::string -LegibleNames:: -getQualified( - OverloadSet const& os, - char delim) const -{ - if(! impl_) - return getUnqualified(os); - std::string result; - if(os.Parent != SymbolID::global) - { - impl_->getLegibleQualified(result, os.Parent, delim); - result.push_back(delim); - } - // the legible name for an overload set is the unqualified - // legible name of its members, without any disambiguation characters. - // members of an overload set use the same legible name regardless of - // whether they belong to an overload set - result.append(impl_->getUnqualified( - os.Members.front())); - return result; -} - -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Support/LegibleNames.hpp b/src/lib/Support/LegibleNames.hpp index daacd01e0d..537fdb290a 100644 --- a/src/lib/Support/LegibleNames.hpp +++ b/src/lib/Support/LegibleNames.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -16,22 +17,23 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { -/** A table mapping symbolIDs to legible names. +/** A table mapping Info objects to legible names. - A legible name for a symbol is case-insensitive - unique and only contains characters which are - valid for both filenames and URL paths. For - filenames this includes only the subset of + A legible name for a symbol is: + + @li case-insensitive + @li unique + @li only characters valid for both filenames and URL paths. + + For filenames this includes only the subset of characters valid for Windows, OSX, and Linux type filesystems. */ class LegibleNames { class Impl; - std::unique_ptr impl_; public: @@ -44,28 +46,21 @@ class LegibleNames Corpus const& corpus, bool enabled); + /** Destructor. + */ ~LegibleNames() noexcept; + /** Return the legible name for a symbol name. + */ std::string - getUnqualified( - SymbolID const& id) const; - - std::string - getUnqualified( - OverloadSet const& os) const; + getUnqualified(SymbolID const& id) const; std::string getQualified( SymbolID const& id, char delim = '-') const; - - std::string - getQualified( - OverloadSet const& os, - char delim = '-') const; }; -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/src/lib/Support/NameParser.cpp b/src/lib/Support/NameParser.cpp index 2abb38bdb3..9cdc75b640 100644 --- a/src/lib/Support/NameParser.cpp +++ b/src/lib/Support/NameParser.cpp @@ -11,17 +11,16 @@ #include "NameParser.hpp" #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { namespace { class TokenStream { - const char* first_; - const char* part_; - const char* ptr_; - const char* last_; + char const* first_; + char const* part_; + char const* ptr_; + char const* last_; public: TokenStream(std::string_view str) @@ -481,23 +480,25 @@ class IdExpressionParser Expected parseIdExpression( - std::string_view str, - bool allow_wildcards) + std::string_view const str, + bool const allow_wildcards) { try { ParseResult result; - if(allow_wildcards) + if (allow_wildcards) + { IdExpressionParser(str, result).parse(); - else + } else + { IdExpressionParser(str, result).parse(); + } return std::move(result); } - catch(const Exception& ex) + catch(Exception const& ex) { return Unexpected(ex.error()); } } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Support/RawOstream.hpp b/src/lib/Support/RawOstream.hpp index be9bf7d826..2001193e1c 100644 --- a/src/lib/Support/RawOstream.hpp +++ b/src/lib/Support/RawOstream.hpp @@ -37,7 +37,7 @@ class RawOstream : public llvm::raw_ostream private: void write_impl( - const char *Ptr, size_t Size) override + char const*Ptr, size_t Size) override { os_.write(Ptr, Size); } diff --git a/src/lib/clang.natvis b/src/lib/clang.natvis index 30153a7756..99226d36f8 100644 --- a/src/lib/clang.natvis +++ b/src/lib/clang.natvis @@ -1060,7 +1060,7 @@ For later versions of Visual Studio, no setup is required--> - {(const char*)BeginX,[Size]s8} - (const char*)BeginX,[Size] + {(char const*)BeginX,[Size]s8} + (char const*)BeginX,[Size] Size Capacity @@ -240,7 +240,7 @@ For later versions of Visual Studio, no setup is required. - + {{ key={key_data(),s8}, value={second,na} }} empty diff --git a/src/test/TestArgs.cpp b/src/test/TestArgs.cpp index 84c3c069d3..0676167482 100644 --- a/src/test/TestArgs.cpp +++ b/src/test/TestArgs.cpp @@ -11,8 +11,7 @@ #include "TestArgs.hpp" #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { TestArgs TestArgs::instance_; @@ -57,7 +56,7 @@ R"( void TestArgs:: -hideForeignOptions() +hideForeignOptions() const { // VFALCO When adding an option, it must // also be added to this list or else it @@ -71,15 +70,17 @@ hideForeignOptions() // Really hide the clang/llvm default // options which we didn't ask for. - auto optionMap = llvm::cl::getRegisteredOptions(); - for(auto& opt : optionMap) + for (auto optionMap = llvm::cl::getRegisteredOptions(); + auto& opt : optionMap) { - if(std::find(ours.begin(), ours.end(), opt.getValue()) != ours.end()) + if (std::ranges::find(ours, opt.getValue()) != ours.end()) + { opt.getValue()->setHiddenFlag(llvm::cl::NotHidden); - else + } else + { opt.getValue()->setHiddenFlag(llvm::cl::ReallyHidden); + } } } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/test/TestArgs.hpp b/src/test/TestArgs.hpp index 4dc148d2b3..2ee9f2b17d 100644 --- a/src/test/TestArgs.hpp +++ b/src/test/TestArgs.hpp @@ -43,7 +43,7 @@ class TestArgs : public PublicToolArgs llvm::cl::opt unitOption; // Hide all options that don't belong to us - void hideForeignOptions(); + void hideForeignOptions() const; }; /** Command line arguments passed to the tool. diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index a57617663c..de9812bb9b 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -118,10 +118,7 @@ handleFile( llvm::StringRef(parentDir), SingleFileDB(filePath), config, defaultIncludePaths); report::debug("Building Corpus", filePath); - auto prev = report::getMinimumLevel(); - report::setMinimumLevel(report::Level::error); auto corpus = CorpusImpl::build(config, compilations); - report::setMinimumLevel(prev); if (!corpus) { return report::error("{}: \"{}\"", corpus.error(), filePath); diff --git a/src/test/lib/Dom/LazyObject.cpp b/src/test/lib/Dom/LazyObject.cpp index 5b504ce93f..f280be3fa6 100644 --- a/src/test/lib/Dom/LazyObject.cpp +++ b/src/test/lib/Dom/LazyObject.cpp @@ -8,7 +8,7 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "lib/Dom/LazyObject.hpp" +#include #include namespace clang { diff --git a/src/tool/ToolMain.cpp b/src/tool/ToolMain.cpp index 9849ff508e..e02a673d47 100644 --- a/src/tool/ToolMain.cpp +++ b/src/tool/ToolMain.cpp @@ -169,18 +169,18 @@ reportUnhandledException( int main(int argc, char const** argv) { - try - { + // try + // { return clang::mrdocs::mrdocs_main(argc, argv); - } - catch(clang::mrdocs::Exception const& ex) - { + // } + // catch(clang::mrdocs::Exception const& ex) + // { // Thrown Exception should never get here. - clang::mrdocs::reportUnhandledException(ex); - } - catch(std::exception const& ex) - { - clang::mrdocs::reportUnhandledException(ex); - } - return EXIT_FAILURE; + // clang::mrdocs::reportUnhandledException(ex); + // } + // catch(std::exception const& ex) + // { + // clang::mrdocs::reportUnhandledException(ex); + // } + // return EXIT_FAILURE; } diff --git a/test-files/golden-tests/filters/symbol-name/excluded-base-class.adoc b/test-files/golden-tests/filters/symbol-name/excluded-base-class.adoc index 24f0bb85d6..f930bb882a 100644 --- a/test-files/golden-tests/filters/symbol-name/excluded-base-class.adoc +++ b/test-files/golden-tests/filters/symbol-name/excluded-base-class.adoc @@ -49,6 +49,7 @@ class D |=== | Name +| <> | <> [.small]#[virtual]# | <> [.small]#[virtual]# | <> @@ -56,6 +57,21 @@ class D +[#A-D-f] +== <>::<>::f + + +=== Synopsis + + +Declared in `<excluded‐base‐class.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f(); +---- + [#A-D-g] == <>::<>::g @@ -124,12 +140,45 @@ class E |=== | Name +| <> +| <> [.small]#[virtual]# | <> [.small]#[virtual]# | <> |=== +[#A-E-f] +== <>::<>::f + + +=== Synopsis + + +Declared in `<excluded‐base‐class.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f(); +---- + +[#A-E-g] +== <>::<>::g + + +=== Synopsis + + +Declared in `<excluded‐base‐class.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +virtual +void +g(); +---- + [#A-E-h] == <>::<>::h diff --git a/test-files/golden-tests/filters/symbol-name/excluded-base-class.html b/test-files/golden-tests/filters/symbol-name/excluded-base-class.html index 48c8f571c9..6d1f30a401 100644 --- a/test-files/golden-tests/filters/symbol-name/excluded-base-class.html +++ b/test-files/golden-tests/filters/symbol-name/excluded-base-class.html @@ -64,6 +64,7 @@

Member Functions

+f g [virtual] h [virtual] i @@ -71,6 +72,22 @@

Member Functions

+
+
+
+

A::D::f

+
+
+

Synopsis

+
+Declared in <excluded-base-class.cpp>
+
+
+void
+f();
+
+
+
@@ -146,12 +163,47 @@

Member Functions

+f +g [virtual] h [virtual] i +
+
+
+

A::E::f

+
+
+

Synopsis

+
+Declared in <excluded-base-class.cpp>
+
+
+void
+f();
+
+
+
+
+
+
+

A::E::g

+
+
+

Synopsis

+
+Declared in <excluded-base-class.cpp>
+
+
+virtual
+void
+g();
+
+
+
diff --git a/test-files/golden-tests/filters/symbol-name/excluded-base-class.xml b/test-files/golden-tests/filters/symbol-name/excluded-base-class.xml index 884ad6ea23..e33b542804 100644 --- a/test-files/golden-tests/filters/symbol-name/excluded-base-class.xml +++ b/test-files/golden-tests/filters/symbol-name/excluded-base-class.xml @@ -8,6 +8,9 @@ + + + @@ -25,6 +28,13 @@ + + + + + + + diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.xml b/test-files/golden-tests/filters/symbol-name/extraction-mode.xml index 0739e097b6..2300ed720a 100644 --- a/test-files/golden-tests/filters/symbol-name/extraction-mode.xml +++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.xml @@ -2,12 +2,35 @@ + + + + An implementation-defined namespace + + + Members are not traversed and, if traversed for some other reason, they also become implementation-defined. + + + A regular namespace with different filters for members + + + + + A symbol that passes the implementation-defined filter + + + A symbol that passes the filters and the implementation-defined filter The symbol is implementation defined and should not have a page. + Members of an implementation-defined scope should not be traversed. If they are traversed for some other reason, they should also become + implementation-defined. + + + @@ -35,20 +58,6 @@ - - - - - - - - A function to get a regular symbol - - - When used in a function, the symbol should be shown as usual with a link to the page. - - - @@ -61,35 +70,20 @@ - - + + - + - A function to get a see-below symbol + A function to get an excluded symbol - When used in a function, the symbol name should be shown as usual. The page for this symbol is what should be different because - the synopsis should say "See below" and the members are not listed unless it's a namespace or the symbol has been explicitly - used as a dependency elsewhere. + When used in a function, only the symbol name should be shown. No links should be generated for this symbol. - - - - - A symbol that passes the implementation-defined filter - - - A symbol that passes the filters and the implementation-defined filter The symbol is implementation defined and should not have a page. - Members of an implementation-defined scope should not be traversed. If they are traversed for some other reason, they should also become - implementation-defined. - - - @@ -107,17 +101,33 @@ - - + + - + - A function to get an excluded symbol + A function to get a regular symbol - When used in a function, only the symbol name should be shown. No links should be generated for this symbol. + When used in a function, the symbol should be shown as usual with a link to the page. + + + + + + + + + + + A function to get a see-below symbol + + + When used in a function, the symbol name should be shown as usual. The page for this symbol is what should be different because + the synopsis should say "See below" and the members are not listed unless it's a namespace or the symbol has been explicitly + used as a dependency elsewhere. @@ -134,6 +144,21 @@ The documentation page for these symbols should include the see-below comment. + + + + + Implementation-defined symbol in a see-below namespace + + + The symbol does not become see-below because the the implentation-defined filter has precedence over the + see-below filter. + + + Functions using this symbol should explain the implementation-defined nature of the symbol. + + + @@ -156,21 +181,24 @@ - - + + + + + - Implementation-defined symbol in a see-below namespace + A function to get a dependency symbol in a see-below namespace - The symbol does not become see-below because the the implentation-defined filter has precedence over the - see-below filter. + The symbol should be extracted as a dependency because the exclude filter has precedence over the see-below filter. + Only included symbols can be promoted to see-below. - Functions using this symbol should explain the implementation-defined nature of the symbol. + It's the responsibility of the function documentation to explain the dependency. - + @@ -188,65 +216,18 @@ - - - - - - - - A function to get a dependency symbol in a see-below namespace - - - The symbol should be extracted as a dependency because the exclude filter has precedence over the see-below filter. - Only included symbols can be promoted to see-below. - - - It's the responsibility of the function documentation to explain the dependency. - - - - - - - - - Namespace alias to form a dependency on the see-below namespace - - - The alias should be linked as usual and, because it's a namespace, the members should be listed on the page. - - - - - - - - An implementation-defined namespace - - - Members are not traversed and, if traversed for some other reason, they also become implementation-defined. - - - - + + - Namespace alias to form a dependency on the implementation-defined namespace + An implementation-defined symbol in the global namespace - - - - - - - Namespace alias to form the dependency on dependency_ns + The symbol is implementation defined and should not have a page. - - + @@ -274,20 +255,6 @@ - - - - - - - - A function to get a regular symbol in the global namespace - - - When used in a function, the symbol should be shown as usual with a link to the page. - - - @@ -302,33 +269,20 @@ - - + + - + - A function to get a see-below symbol in the global namespace + A function to get a dependency symbol on the global namespace - When used in a function, the symbol name should be shown as usual. The page for this symbol is what should be different because - the synopsis should say "See below" and the members are not listed unless it's a namespace or the symbol has been explicitly - used as a dependency elsewhere. + The symbol should be extracted as a dependency but its members should not be traversed. - - - - - An implementation-defined symbol in the global namespace - - - The symbol is implementation defined and should not have a page. - - - @@ -346,19 +300,65 @@ - - + + - + - A function to get a dependency symbol on the global namespace + A function to get a regular symbol in the global namespace - The symbol should be extracted as a dependency but its members should not be traversed. + When used in a function, the symbol should be shown as usual with a link to the page. + + + + + + + + A function to get a see-below symbol in the global namespace + + + When used in a function, the symbol name should be shown as usual. The page for this symbol is what should be different because + the synopsis should say "See below" and the members are not listed unless it's a namespace or the symbol has been explicitly + used as a dependency elsewhere. + + + + + + + + Namespace alias to form the dependency on dependency_ns + + + + + + + + + Namespace alias to form a dependency on the implementation-defined namespace + + + + + + + + + Namespace alias to form a dependency on the see-below namespace + + + The alias should be linked as usual and, because it's a namespace, the members should be listed on the page. + + + + diff --git a/test-files/golden-tests/filters/symbol-name/whitelist_0.xml b/test-files/golden-tests/filters/symbol-name/whitelist_0.xml index 3c636fc03d..9a7b5f4dba 100644 --- a/test-files/golden-tests/filters/symbol-name/whitelist_0.xml +++ b/test-files/golden-tests/filters/symbol-name/whitelist_0.xml @@ -79,19 +79,19 @@ N5::N6::*7 - + This namespace should be included because it matches N5::N6::*7 - - + + This function should be included because it's a member of - N7 + M7 , which matches N5::N6::*7 @@ -99,19 +99,19 @@ - + This namespace should be included because it matches N5::N6::*7 - - + + This function should be included because it's a member of - M7 + N7 , which matches N5::N6::*7 @@ -129,15 +129,6 @@ C - - - - - This function should be included because it's a member of - C - - - @@ -156,6 +147,15 @@ + + + + + This function should be included because it's a member of + C + + + diff --git a/test-files/golden-tests/javadoc/brief-3.adoc b/test-files/golden-tests/javadoc/brief-3.adoc index b5ef0aa3e0..5a2833dc60 100644 --- a/test-files/golden-tests/javadoc/brief-3.adoc +++ b/test-files/golden-tests/javadoc/brief-3.adoc @@ -7,24 +7,17 @@ === Functions -[cols=2] +[cols=1] |=== -| Name | Description - -| <> -| C string overload. - -Integer overload. - +| Name +| <> |=== -[#f] +[#f-0e] == f -Integer overload. - === Synopsis @@ -62,13 +55,6 @@ void [.small]#<># -=== Description - - -More info - - - [#f-06] == f diff --git a/test-files/golden-tests/javadoc/brief-3.html b/test-files/golden-tests/javadoc/brief-3.html index 1ef31e484d..9f3b73e75d 100644 --- a/test-files/golden-tests/javadoc/brief-3.html +++ b/test-files/golden-tests/javadoc/brief-3.html @@ -13,29 +13,18 @@

Functions

- + - +
NameDescriptionName
f C string overload. - - -Integer overload. - - -
f
-

f

-
-Integer overload. - - -
+

f

Synopsis

@@ -70,12 +59,6 @@

Synopsis

» more... -
-
-

Description

-

More info

- -
diff --git a/test-files/golden-tests/javadoc/ref.xml b/test-files/golden-tests/javadoc/ref.xml index b6c028c149..35132d96c4 100644 --- a/test-files/golden-tests/javadoc/ref.xml +++ b/test-files/golden-tests/javadoc/ref.xml @@ -2,24 +2,7 @@ - - - - - - - - See - f0 - - - See - ::f0 - - - - - + diff --git a/test-files/golden-tests/metadata/class-template-specializations-1.xml b/test-files/golden-tests/metadata/class-template-specializations-1.xml index 747228035c..b2bb4c4682 100644 --- a/test-files/golden-tests/metadata/class-template-specializations-1.xml +++ b/test-files/golden-tests/metadata/class-template-specializations-1.xml @@ -2,27 +2,666 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - + + + - - - + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + - - - - - - + + + - - - - - - + + + - - - - - - - - - + + + - - - - - - + + + - - - - - - + + + + + + + + + - - - - - - + + + + + + - - - + + + + + + + + + - - - - - - + + + + + + + + + - - - - - - diff --git a/test-files/golden-tests/metadata/class-template-specializations-3.xml b/test-files/golden-tests/metadata/class-template-specializations-3.xml index 732839caee..f44b4f77b2 100644 --- a/test-files/golden-tests/metadata/class-template-specializations-3.xml +++ b/test-files/golden-tests/metadata/class-template-specializations-3.xml @@ -273,6 +273,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -305,26 +325,6 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/test-files/golden-tests/metadata/class-template.xml b/test-files/golden-tests/metadata/class-template.xml index 5dd44c7475..4661d7d58d 100644 --- a/test-files/golden-tests/metadata/class-template.xml +++ b/test-files/golden-tests/metadata/class-template.xml @@ -2,13 +2,17 @@ - - - - - - - + + + + + + + diff --git a/test-files/golden-tests/metadata/concept.xml b/test-files/golden-tests/metadata/concept.xml index 3f81e5cc2e..953a72be03 100644 --- a/test-files/golden-tests/metadata/concept.xml +++ b/test-files/golden-tests/metadata/concept.xml @@ -2,12 +2,6 @@ - - - - - + + + + diff --git a/test-files/golden-tests/metadata/dependency-propagation.xml b/test-files/golden-tests/metadata/dependency-propagation.xml index af409ea52d..24997319fc 100644 --- a/test-files/golden-tests/metadata/dependency-propagation.xml +++ b/test-files/golden-tests/metadata/dependency-propagation.xml @@ -9,6 +9,9 @@ + + + - - - diff --git a/test-files/golden-tests/metadata/explicit-conv-operator.xml b/test-files/golden-tests/metadata/explicit-conv-operator.xml index b1380789f4..0c30f1dfe0 100644 --- a/test-files/golden-tests/metadata/explicit-conv-operator.xml +++ b/test-files/golden-tests/metadata/explicit-conv-operator.xml @@ -11,6 +11,18 @@ + @@ -29,17 +41,5 @@ - diff --git a/test-files/golden-tests/metadata/explicit-ctor.adoc b/test-files/golden-tests/metadata/explicit-ctor.adoc index 127d43c44c..1e2cfe38d7 100644 --- a/test-files/golden-tests/metadata/explicit-ctor.adoc +++ b/test-files/golden-tests/metadata/explicit-ctor.adoc @@ -37,12 +37,12 @@ struct Explicit; |=== | Name -| <> [.small]#[constructor]# +| <> [.small]#[constructor]# |=== -[#Explicit-2constructor] +[#Explicit-2constructor-08] == <>::Explicit @@ -168,12 +168,12 @@ struct ExplicitExpression; |=== | Name -| <> [.small]#[constructor]# +| <> [.small]#[constructor]# |=== -[#ExplicitExpression-2constructor] +[#ExplicitExpression-2constructor-026] == <>::ExplicitExpression @@ -209,12 +209,12 @@ explicit(B) [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- explicit(B) -<>( +<>( int, int); ---- -[.small]#<># +[.small]#<># [#ExplicitExpression-2constructor-0b] == <>::ExplicitExpression @@ -261,7 +261,7 @@ explicit(B) ExplicitExpression(<>&&) noexcept; ---- -[#ExplicitExpression-2constructor-02] +[#ExplicitExpression-2constructor-027] == <>::ExplicitExpression @@ -298,12 +298,12 @@ struct ExplicitFalse; |=== | Name -| <> [.small]#[constructor]# +| <> [.small]#[constructor]# |=== -[#ExplicitFalse-2constructor] +[#ExplicitFalse-2constructor-04c] == <>::ExplicitFalse @@ -339,12 +339,12 @@ explicit(false) [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- explicit(false) -<>( +<>( int, int); ---- -[.small]#<># +[.small]#<># [#ExplicitFalse-2constructor-01] == <>::ExplicitFalse @@ -391,7 +391,7 @@ explicit(false) ExplicitFalse(<>&&) noexcept; ---- -[#ExplicitFalse-2constructor-04] +[#ExplicitFalse-2constructor-04f] == <>::ExplicitFalse @@ -428,12 +428,12 @@ struct ExplicitTrue; |=== | Name -| <> [.small]#[constructor]# +| <> [.small]#[constructor]# |=== -[#ExplicitTrue-2constructor] +[#ExplicitTrue-2constructor-0f] == <>::ExplicitTrue diff --git a/test-files/golden-tests/metadata/explicit-ctor.html b/test-files/golden-tests/metadata/explicit-ctor.html index dc1e6f4146..790ca5d019 100644 --- a/test-files/golden-tests/metadata/explicit-ctor.html +++ b/test-files/golden-tests/metadata/explicit-ctor.html @@ -48,7 +48,7 @@

Member Functions

-Explicit [constructor] +Explicit [constructor] @@ -56,7 +56,7 @@

Member Functions

-

Explicit::Explicit

+

Explicit::Explicit

Synopsis

@@ -185,7 +185,7 @@

Member Functions

-ExplicitExpression [constructor] +ExplicitExpression [constructor] @@ -193,7 +193,7 @@

Member Functions

-

ExplicitExpression::ExplicitExpression

+

ExplicitExpression::ExplicitExpression

Synopsis

@@ -223,11 +223,11 @@

Synopsis

 
 explicit(B)
-ExplicitExpression(
+ExplicitExpression(
     int,
     int);
 
-
» more... +» more...
@@ -282,7 +282,7 @@

Synopsis

-

ExplicitExpression::ExplicitExpression

+

ExplicitExpression::ExplicitExpression

Synopsis

@@ -321,7 +321,7 @@

Member Functions

-ExplicitFalse [constructor] +ExplicitFalse [constructor] @@ -329,7 +329,7 @@

Member Functions

-

ExplicitFalse::ExplicitFalse

+

ExplicitFalse::ExplicitFalse

Synopsis

@@ -359,11 +359,11 @@

Synopsis

 
 explicit(false)
-ExplicitFalse(
+ExplicitFalse(
     int,
     int);
 
-
» more... +» more...
@@ -418,7 +418,7 @@

Synopsis

-

ExplicitFalse::ExplicitFalse

+

ExplicitFalse::ExplicitFalse

Synopsis

@@ -457,7 +457,7 @@

Member Functions

-ExplicitTrue [constructor] +ExplicitTrue [constructor] @@ -465,7 +465,7 @@

Member Functions

-

ExplicitTrue::ExplicitTrue

+

ExplicitTrue::ExplicitTrue

Synopsis

diff --git a/test-files/golden-tests/metadata/explicit-ctor.xml b/test-files/golden-tests/metadata/explicit-ctor.xml index 53d96a902c..38c4b96d26 100644 --- a/test-files/golden-tests/metadata/explicit-ctor.xml +++ b/test-files/golden-tests/metadata/explicit-ctor.xml @@ -33,29 +33,63 @@ - - - - + + + + + - - + + - + - - + + - + - - + + @@ -64,29 +98,29 @@ - - - - + + + + - - + + - + - - + + - + - - + + @@ -95,39 +129,5 @@ - diff --git a/test-files/golden-tests/metadata/explicit-deduct-guide.xml b/test-files/golden-tests/metadata/explicit-deduct-guide.xml index f3a3649a3b..ea30611ab6 100644 --- a/test-files/golden-tests/metadata/explicit-deduct-guide.xml +++ b/test-files/golden-tests/metadata/explicit-deduct-guide.xml @@ -8,6 +8,18 @@ + @@ -35,17 +47,5 @@ - diff --git a/test-files/golden-tests/metadata/explicit-object-parameter.adoc b/test-files/golden-tests/metadata/explicit-object-parameter.adoc index 23330198d9..ee7f195b63 100644 --- a/test-files/golden-tests/metadata/explicit-object-parameter.adoc +++ b/test-files/golden-tests/metadata/explicit-object-parameter.adoc @@ -34,12 +34,12 @@ struct Optional; |=== | Name -| <> +| <> |=== -[#Optional-value] +[#Optional-value-01] == <>::value diff --git a/test-files/golden-tests/metadata/explicit-object-parameter.html b/test-files/golden-tests/metadata/explicit-object-parameter.html index e83cb50cfc..d7ea4e3eb1 100644 --- a/test-files/golden-tests/metadata/explicit-object-parameter.html +++ b/test-files/golden-tests/metadata/explicit-object-parameter.html @@ -45,7 +45,7 @@

Member Functions

-value +value @@ -53,7 +53,7 @@

Member Functions

-

Optional::value

+

Optional::value

Synopsis

diff --git a/test-files/golden-tests/metadata/friend-1.xml b/test-files/golden-tests/metadata/friend-1.xml index be6b9c8679..2263d534ea 100644 --- a/test-files/golden-tests/metadata/friend-1.xml +++ b/test-files/golden-tests/metadata/friend-1.xml @@ -2,14 +2,6 @@ - - - - - f - - - @@ -22,5 +14,13 @@ + + + + + f + + + diff --git a/test-files/golden-tests/metadata/friend-2.xml b/test-files/golden-tests/metadata/friend-2.xml index 427d766370..bbeda2da94 100644 --- a/test-files/golden-tests/metadata/friend-2.xml +++ b/test-files/golden-tests/metadata/friend-2.xml @@ -2,15 +2,6 @@ - - - - - - f - - - @@ -23,5 +14,14 @@ + + + + + + f + + + diff --git a/test-files/golden-tests/metadata/friend-3.xml b/test-files/golden-tests/metadata/friend-3.xml index a802ccac03..c589cfc636 100644 --- a/test-files/golden-tests/metadata/friend-3.xml +++ b/test-files/golden-tests/metadata/friend-3.xml @@ -2,16 +2,6 @@ - - - - - - - T::f - - - @@ -31,5 +21,15 @@ + + + + + + + T::f + + + diff --git a/test-files/golden-tests/metadata/friend-4.xml b/test-files/golden-tests/metadata/friend-4.xml index f7ba46000f..d3f3a6be86 100644 --- a/test-files/golden-tests/metadata/friend-4.xml +++ b/test-files/golden-tests/metadata/friend-4.xml @@ -2,16 +2,6 @@ - - - - - - - U::f - - - @@ -31,5 +21,15 @@ + + + + + + + U::f + + + diff --git a/test-files/golden-tests/metadata/friend-5.xml b/test-files/golden-tests/metadata/friend-5.xml index 5d36761205..46a7f34bf2 100644 --- a/test-files/golden-tests/metadata/friend-5.xml +++ b/test-files/golden-tests/metadata/friend-5.xml @@ -2,16 +2,6 @@ - - - - - - - f - - - @@ -26,5 +16,15 @@ + + + + + + + f + + + diff --git a/test-files/golden-tests/metadata/friend-6.xml b/test-files/golden-tests/metadata/friend-6.xml index 3f3b366fee..bf4f009561 100644 --- a/test-files/golden-tests/metadata/friend-6.xml +++ b/test-files/golden-tests/metadata/friend-6.xml @@ -2,9 +2,6 @@ - - - @@ -12,23 +9,23 @@ Struct T brief - - + + - Friend int brief + Friend class Z brief - + - - + + - Friend class Z brief + Friend int brief - + @@ -65,5 +62,8 @@ + + + diff --git a/test-files/golden-tests/metadata/function-parm-decay.xml b/test-files/golden-tests/metadata/function-parm-decay.xml index 9d8750bf00..1acdf0a59a 100644 --- a/test-files/golden-tests/metadata/function-parm-decay.xml +++ b/test-files/golden-tests/metadata/function-parm-decay.xml @@ -34,14 +34,6 @@ - - - - - - - - @@ -51,5 +43,13 @@ + + + + + + + + diff --git a/test-files/golden-tests/metadata/function-tparm-decay.xml b/test-files/golden-tests/metadata/function-tparm-decay.xml index 9edf9af14e..b90222fb3a 100644 --- a/test-files/golden-tests/metadata/function-tparm-decay.xml +++ b/test-files/golden-tests/metadata/function-tparm-decay.xml @@ -29,14 +29,6 @@ - - - - - - - - + + + + + + + + diff --git a/test-files/golden-tests/metadata/implicit-instantiation-member-ref.xml b/test-files/golden-tests/metadata/implicit-instantiation-member-ref.xml index a435e41db4..1a6de4cb61 100644 --- a/test-files/golden-tests/metadata/implicit-instantiation-member-ref.xml +++ b/test-files/golden-tests/metadata/implicit-instantiation-member-ref.xml @@ -25,10 +25,6 @@ - - - - + + + + diff --git a/test-files/golden-tests/metadata/local-class.xml b/test-files/golden-tests/metadata/local-class.xml index 1b551f08d9..5c158ea920 100644 --- a/test-files/golden-tests/metadata/local-class.xml +++ b/test-files/golden-tests/metadata/local-class.xml @@ -2,13 +2,6 @@ - - - - - - - @@ -16,5 +9,12 @@ + + + + + + + diff --git a/test-files/golden-tests/metadata/mem-fn.xml b/test-files/golden-tests/metadata/mem-fn.xml index 9819d6a33c..fc2fec3a8f 100644 --- a/test-files/golden-tests/metadata/mem-fn.xml +++ b/test-files/golden-tests/metadata/mem-fn.xml @@ -153,6 +153,23 @@ + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/metadata/namespace.xml b/test-files/golden-tests/metadata/namespace.xml index 279d9293b6..0b031b3941 100644 --- a/test-files/golden-tests/metadata/namespace.xml +++ b/test-files/golden-tests/metadata/namespace.xml @@ -2,10 +2,27 @@ - - - + + + + + + + + + + + + + + + + + + + + @@ -16,16 +33,16 @@ - - - - - + + + - - - + + + + + @@ -36,26 +53,9 @@ - - - - - - - - - + + - - - - - - - - - - diff --git a/test-files/golden-tests/metadata/no_unique_address.xml b/test-files/golden-tests/metadata/no_unique_address.xml index c4f9da3a0f..5172e712ca 100644 --- a/test-files/golden-tests/metadata/no_unique_address.xml +++ b/test-files/golden-tests/metadata/no_unique_address.xml @@ -7,16 +7,16 @@ - - - - + + + + diff --git a/test-files/golden-tests/metadata/noreturn.xml b/test-files/golden-tests/metadata/noreturn.xml index f7dc8466b6..7fac2b921d 100644 --- a/test-files/golden-tests/metadata/noreturn.xml +++ b/test-files/golden-tests/metadata/noreturn.xml @@ -2,10 +2,6 @@ - - - - @@ -22,5 +18,9 @@ + + + + diff --git a/test-files/golden-tests/metadata/ns-variables.xml b/test-files/golden-tests/metadata/ns-variables.xml index 31ed2d3d8d..34f2850166 100644 --- a/test-files/golden-tests/metadata/ns-variables.xml +++ b/test-files/golden-tests/metadata/ns-variables.xml @@ -2,6 +2,9 @@ + + + @@ -30,9 +33,6 @@ - - - diff --git a/test-files/golden-tests/metadata/overloads.adoc b/test-files/golden-tests/metadata/overloads.adoc index 96dd00d8da..7204017678 100644 --- a/test-files/golden-tests/metadata/overloads.adoc +++ b/test-files/golden-tests/metadata/overloads.adoc @@ -20,8 +20,8 @@ |=== | Name -| <> -| <> +| <> +| <> |=== [#A] @@ -44,7 +44,7 @@ struct A; |=== | Name -| <> +| <> |=== === Static Member Functions @@ -52,7 +52,7 @@ struct A; |=== | Name -| <> +| <> |=== === Friends @@ -66,7 +66,7 @@ struct A; -[#A-f] +[#A-f-00] == <>::f @@ -121,7 +121,7 @@ int f(int); ---- -[#A-g] +[#A-g-0e] == <>::g @@ -233,7 +233,7 @@ struct B; -[#f] +[#f-0e] == f @@ -288,7 +288,7 @@ int f(int); ---- -[#operator_eq] +[#operator_eq-0d] == operator== diff --git a/test-files/golden-tests/metadata/overloads.html b/test-files/golden-tests/metadata/overloads.html index e030fa1cb1..5468e1f711 100644 --- a/test-files/golden-tests/metadata/overloads.html +++ b/test-files/golden-tests/metadata/overloads.html @@ -31,8 +31,8 @@

Functions

-f -operator== +f +operator==
@@ -59,7 +59,7 @@

Member Functions

-f +f

Static Member Functions

@@ -71,7 +71,7 @@

Static Member Functions

-g +g

Friends

@@ -92,7 +92,7 @@

Friends

-

A::f

+

A::f

Synopsis

@@ -149,7 +149,7 @@

Synopsis

-

A::g

+

A::g

Synopsis

@@ -265,7 +265,7 @@

Synopsis

-

f

+

f

Synopsis

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

Synopsis

-

operator==

+

operator==

Synopsis

diff --git a/test-files/golden-tests/metadata/overloads.xml b/test-files/golden-tests/metadata/overloads.xml index 9b9925856e..482602afda 100644 --- a/test-files/golden-tests/metadata/overloads.xml +++ b/test-files/golden-tests/metadata/overloads.xml @@ -2,32 +2,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -65,6 +39,9 @@ + + + @@ -77,9 +54,32 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/metadata/record-1.xml b/test-files/golden-tests/metadata/record-1.xml index 634e480dca..3d102c4a18 100644 --- a/test-files/golden-tests/metadata/record-1.xml +++ b/test-files/golden-tests/metadata/record-1.xml @@ -4,14 +4,6 @@ - - - - - - - - @@ -72,6 +64,14 @@ + + + + + + + + diff --git a/test-files/golden-tests/metadata/record-access.xml b/test-files/golden-tests/metadata/record-access.xml index 950168d1ad..b860971304 100644 --- a/test-files/golden-tests/metadata/record-access.xml +++ b/test-files/golden-tests/metadata/record-access.xml @@ -2,18 +2,6 @@ - - - - - - - - - - - - @@ -26,6 +14,18 @@ + + + + + + + + + + + + diff --git a/test-files/golden-tests/metadata/record-inheritance.xml b/test-files/golden-tests/metadata/record-inheritance.xml index 6c8ee5ee00..3a819a9a57 100644 --- a/test-files/golden-tests/metadata/record-inheritance.xml +++ b/test-files/golden-tests/metadata/record-inheritance.xml @@ -2,40 +2,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -81,6 +50,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -99,5 +96,8 @@ + + + diff --git a/test-files/golden-tests/metadata/requires-clause.adoc b/test-files/golden-tests/metadata/requires-clause.adoc index e85b2ec547..7664fc45aa 100644 --- a/test-files/golden-tests/metadata/requires-clause.adoc +++ b/test-files/golden-tests/metadata/requires-clause.adoc @@ -21,7 +21,7 @@ | Name | <> -| <> +| <> |=== [#A-0c] @@ -79,7 +79,7 @@ f() requires (sizeof(U) == 2); ---- -[#g] +[#g-0d] == g diff --git a/test-files/golden-tests/metadata/requires-clause.html b/test-files/golden-tests/metadata/requires-clause.html index 2a7403b142..2ead9f0d1e 100644 --- a/test-files/golden-tests/metadata/requires-clause.html +++ b/test-files/golden-tests/metadata/requires-clause.html @@ -32,7 +32,7 @@

Functions

f -g +g
@@ -94,7 +94,7 @@

Synopsis

-

g

+

g

Synopsis

diff --git a/test-files/golden-tests/metadata/requires-clause.xml b/test-files/golden-tests/metadata/requires-clause.xml index b8ef983dea..6fa2abab77 100644 --- a/test-files/golden-tests/metadata/requires-clause.xml +++ b/test-files/golden-tests/metadata/requires-clause.xml @@ -2,6 +2,18 @@ + + - - diff --git a/test-files/golden-tests/metadata/sfinae.xml b/test-files/golden-tests/metadata/sfinae.xml index 27a60ee985..0aaacdc6ef 100644 --- a/test-files/golden-tests/metadata/sfinae.xml +++ b/test-files/golden-tests/metadata/sfinae.xml @@ -2,6 +2,76 @@ + + + + + + + + + + - - - - - - - - - - diff --git a/test-files/golden-tests/metadata/spec-mem-implicit-instantiation.xml b/test-files/golden-tests/metadata/spec-mem-implicit-instantiation.xml index c8f1280cd2..bc6362598f 100644 --- a/test-files/golden-tests/metadata/spec-mem-implicit-instantiation.xml +++ b/test-files/golden-tests/metadata/spec-mem-implicit-instantiation.xml @@ -6,9 +6,6 @@ - - - + + + diff --git a/test-files/golden-tests/metadata/template-specialization-inheritance.xml b/test-files/golden-tests/metadata/template-specialization-inheritance.xml index 8d2fd4615e..1b25dcb5d2 100644 --- a/test-files/golden-tests/metadata/template-specialization-inheritance.xml +++ b/test-files/golden-tests/metadata/template-specialization-inheritance.xml @@ -2,6 +2,27 @@ + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/test-files/golden-tests/metadata/type-resolution.xml b/test-files/golden-tests/metadata/type-resolution.xml index 2b0ad16acf..7cbdf478ba 100644 --- a/test-files/golden-tests/metadata/type-resolution.xml +++ b/test-files/golden-tests/metadata/type-resolution.xml @@ -12,21 +12,6 @@ - - - - - - - - - @@ -397,5 +382,20 @@ + + + + + + + + + diff --git a/test-files/golden-tests/metadata/using-3.adoc b/test-files/golden-tests/metadata/using-3.adoc index 6e7e647704..401186dad5 100644 --- a/test-files/golden-tests/metadata/using-3.adoc +++ b/test-files/golden-tests/metadata/using-3.adoc @@ -118,7 +118,7 @@ struct C |=== | Name -| <> +| <> |=== === Using Declarations @@ -126,13 +126,68 @@ struct C |=== | Name -| <> +| <> | <> |=== -[#C-f-08] +[#C-f-082] +== <>::f + + +=== Synopsis + + +Declared in `<using‐3.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +<>(int); +---- + +[.small]#<># + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +<>(bool); +---- + +[.small]#<># + +[#A-f] +== <>::f + + +=== Synopsis + + +Declared in `<using‐3.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f(int); +---- + +[#B-f] +== <>::f + + +=== Synopsis + + +Declared in `<using‐3.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f(bool); +---- + +[#C-f-081] == <>::f diff --git a/test-files/golden-tests/metadata/using-3.html b/test-files/golden-tests/metadata/using-3.html index ae1ef09c5a..a0d725016d 100644 --- a/test-files/golden-tests/metadata/using-3.html +++ b/test-files/golden-tests/metadata/using-3.html @@ -139,7 +139,7 @@

Member Functions

-f +f

Using Declarations

@@ -151,7 +151,7 @@

Using Declarations

-f +f f @@ -160,7 +160,64 @@

Using Declarations

-

C::f

+

C::f

+
+
+

Synopsis

+
+Declared in <using-3.cpp>
+
+
+void
+f(int);
+
+
» more... + +
+
+void
+f(bool);
+
+
» more... + + +
+
+
+
+

A::f

+
+
+

Synopsis

+
+Declared in <using-3.cpp>
+
+
+void
+f(int);
+
+
+
+
+
+
+

B::f

+
+
+

Synopsis

+
+Declared in <using-3.cpp>
+
+
+void
+f(bool);
+
+
+
+
+
+
+

C::f

Synopsis

diff --git a/test-files/golden-tests/metadata/using-3.xml b/test-files/golden-tests/metadata/using-3.xml index 44115fd5bb..b9ba514a37 100644 --- a/test-files/golden-tests/metadata/using-3.xml +++ b/test-files/golden-tests/metadata/using-3.xml @@ -28,6 +28,18 @@ + + + + + + + + + + + + diff --git a/test-files/golden-tests/metadata/var-inline-constexpr.xml b/test-files/golden-tests/metadata/var-inline-constexpr.xml index f07c484ee4..4ce555c94b 100644 --- a/test-files/golden-tests/metadata/var-inline-constexpr.xml +++ b/test-files/golden-tests/metadata/var-inline-constexpr.xml @@ -2,39 +2,26 @@ - - - - - - - - - + + - - - - - - - - - + + + + + + + + + + + + + + + +