diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index cb1bc1071..c7e8ec464 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -13,11 +13,11 @@ #define MRDOCS_API_METADATA_INFO_HPP #include +#include #include #include #include -#include -#include +#include #include #include #include @@ -174,50 +174,36 @@ struct IsInfo : Info */ template< class InfoTy, - class F, + class Fn, class... Args> requires std::derived_from decltype(auto) visit( - InfoTy& I, - F&& f, + InfoTy& info, + Fn&& fn, Args&&... args) { - add_cv_from_t& II = I; - switch(I.Kind) + auto visitor = makeVisitor( + info, std::forward(fn), + std::forward(args)...); + switch(info.Kind) { case InfoKind::Namespace: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Record: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Function: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Enum: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Typedef: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Variable: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Field: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); case InfoKind::Specialization: - return f(static_cast&>(II), - std::forward(args)...); + return visitor.template visit(); default: MRDOCS_UNREACHABLE(); } diff --git a/include/mrdocs/Metadata/Javadoc.hpp b/include/mrdocs/Metadata/Javadoc.hpp index 10152d007..63c634161 100644 --- a/include/mrdocs/Metadata/Javadoc.hpp +++ b/include/mrdocs/Metadata/Javadoc.hpp @@ -15,7 +15,9 @@ #include #include +#include #include +#include #include #include #include @@ -55,6 +57,8 @@ enum class Kind returns, styled, tparam, + reference, + copied }; /** A text style. @@ -81,7 +85,7 @@ enum class Admonish /** Parameter pass direction. */ -enum class ParamDirection : int +enum class ParamDirection { none, in, @@ -89,6 +93,19 @@ enum class ParamDirection : int inout }; +/** Which parts of the documentation to copy. + + @li `all`: copy the brief and the description. + @li `brief`: only copy the brief. + @li `description`: only copy the description. +*/ +enum class Parts +{ + all = 1, // needed by bitstream + brief, + description +}; + //-------------------------------------------- /** This is a variant-like list element. @@ -208,6 +225,61 @@ struct Link : Text } }; +/** A reference to a symbol. +*/ +struct Reference : Text +{ + SymbolID id = SymbolID::zero; + + static constexpr Kind static_kind = Kind::reference; + + explicit + Reference( + String string_ = String()) noexcept + : Text(std::move(string_), Kind::reference) + { + } + + bool operator==(const Reference&) const noexcept = default; + bool equals(const Node& other) const noexcept override + { + return kind == other.kind && + *this == static_cast(other); + } + +protected: + Reference( + String string_, + Kind kind_) noexcept + : Text(std::move(string_), kind_) + { + } +}; + +/** Documentation copied from another symbol. +*/ +struct Copied : Reference +{ + Parts parts; + + static constexpr Kind static_kind = Kind::copied; + + Copied( + String string_ = String(), + Parts parts_ = Parts::all) noexcept + : Reference(std::move(string_), Kind::copied) + , parts(parts_) + { + } + + bool operator==(Copied const&) const noexcept = default; + bool equals(Node const& other) const noexcept override + { + return kind == other.kind && + *this == static_cast(other); + } +}; + //------------------------------------------------ // // Block nodes @@ -247,9 +319,10 @@ struct MRDOCS_DECL } template T> - void emplace_back(T&& text) + T& emplace_back(T&& text) { - emplace_back(std::make_unique(std::move(text))); + return static_cast(emplace_back( + std::make_unique(std::move(text)))); } void append(List&& blocks); @@ -265,7 +338,7 @@ struct MRDOCS_DECL } private: - void emplace_back(std::unique_ptr text); + Text& emplace_back(std::unique_ptr text); }; /** A manually specified section heading. @@ -497,6 +570,10 @@ visit( return f.template operator()(std::forward(args)...); case Kind::link: return f.template operator()(std::forward(args)...); + case Kind::reference: + return f.template operator()(std::forward(args)...); + case Kind::copied: + return f.template operator()(std::forward(args)...); case Kind::list_item: return f.template operator()(std::forward(args)...); case Kind::paragraph: @@ -516,51 +593,50 @@ visit( } } -template -constexpr -auto +template< + class NodeTy, + class Fn, + class... Args> + requires std::derived_from +decltype(auto) visit( - Node const& node, - F&& f, Args&&... args) + NodeTy& node, + Fn&& fn, + Args&&... args) { + auto visitor = makeVisitor( + node, std::forward(fn), + std::forward(args)...); switch(node.kind) { case Kind::admonition: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::brief: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::code: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::heading: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::paragraph: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::link: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); + case Kind::reference: + return visitor.template visit(); + case Kind::copied: + return visitor.template visit(); case Kind::list_item: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::param: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::returns: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::styled: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::text: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); case Kind::tparam: - return f(static_cast(node), - std::forward(args)...); + return visitor.template visit(); default: MRDOCS_UNREACHABLE(); } @@ -593,6 +669,8 @@ MRDOCS_DECL dom::String toString(Style style) noexcept; //------------------------------------------------ +class Corpus; + /** A processed Doxygen-style comment attached to a declaration. */ class MRDOCS_DECL @@ -621,7 +699,10 @@ class MRDOCS_DECL /** Return the brief, or nullptr if there is none. */ doc::Paragraph const* - brief() const noexcept; + getBrief(Corpus const& corpus) const noexcept; + + doc::List const& + getDescription(Corpus const& corpus) const noexcept; /** Return the list of top level blocks. */ @@ -665,7 +746,8 @@ class MRDOCS_DECL the returend overview is invalidated if the javadoc object is destroyed. */ - doc::Overview makeOverview() const; + doc::Overview + makeOverview(const Corpus& corpus) const; //-------------------------------------------- @@ -692,7 +774,6 @@ class MRDOCS_DECL private: std::string emplace_back(std::unique_ptr); - doc::Paragraph const* brief_ = nullptr; doc::List blocks_; }; diff --git a/include/mrdocs/Support/Visitor.hpp b/include/mrdocs/Support/Visitor.hpp new file mode 100644 index 000000000..6ad1492c7 --- /dev/null +++ b/include/mrdocs/Support/Visitor.hpp @@ -0,0 +1,80 @@ +// +// 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) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_TOOL_SUPPORT_VISITOR_HPP +#define MRDOCS_TOOL_SUPPORT_VISITOR_HPP + +#include +#include +#include + +namespace clang { +namespace mrdocs { + +template< + typename Base, + typename Fn, + typename... Args> +class Visitor +{ + Base&& obj_; + Fn&& fn_; + std::tuple args_; + + template + decltype(auto) + getAs() + { + return static_cast&&>(obj_); + } + +public: + Visitor(Base&& obj, Fn&& fn, Args&&... args) + : obj_(std::forward(obj)) + , fn_(std::forward(fn)) + , args_(std::forward(args)...) + { + } + + template + requires std::derived_from> + decltype(auto) + visit() + { + return std::apply(std::forward(fn_), std::tuple_cat( + std::forward_as_tuple(getAs()), args_)); + } +}; + +template< + typename BaseTy, + typename ObjectTy, + typename FnTy, + typename... ArgsTy> +auto +makeVisitor( + ObjectTy&& obj, + FnTy&& fn, + ArgsTy&&... args) +{ + using VisitorTy = Visitor< + add_cvref_from_t, + FnTy&&, ArgsTy&&...>; + return VisitorTy(std::forward(obj), + std::forward(fn), + std::forward(args)...); +} + +} // mrdocs +} // clang + +#endif diff --git a/include/mrdocs/mrdocs.natvis b/include/mrdocs/mrdocs.natvis index fd676a280..d430e9a5e 100644 --- a/include/mrdocs/mrdocs.natvis +++ b/include/mrdocs/mrdocs.natvis @@ -14,24 +14,33 @@ - {data_[0],nvoXb}{data_[1],nvoXb}{data_[2],nvoXb}{data_[3],nvoXb}{data_[4],nvoXb}{data_[5],nvoXb}{data_[6],nvoXb}{data_[7],nvoXb}{data_[8],nvoXb}{data_[9],nvoXb}{data_[10],nvoXb}{data_[11],nvoXb}{data_[12],nvoXb}{data_[13],nvoXb}{data_[14],nvoXb}{data_[15],nvoXb}{data_[16],nvoXb}{data_[17],nvoXb}{data_[18],nvoXb}{data_[19],nvoXb} - empty SymbolID + {data_[0],nvoXb}{data_[1],nvoXb}{data_[2],nvoXb}{data_[3],nvoXb}{data_[4],nvoXb}{data_[5],nvoXb}{data_[6],nvoXb}{data_[7],nvoXb}{data_[8],nvoXb}{data_[9],nvoXb}{data_[10],nvoXb}{data_[11],nvoXb}{data_[12],nvoXb}{data_[13],nvoXb}{data_[14],nvoXb}{data_[15],nvoXb}{data_[16],nvoXb}{data_[17],nvoXb}{data_[18],nvoXb}{data_[19],nvoXb} + empty SymbolID - {t_} - empty + {t_} + empty + + {reason_} + + {val_} - {->unex_} + {unex_} + + + val_ + unex_ + diff --git a/mrdocs.rnc b/mrdocs.rnc index fcdbf5db5..bd0566e9d 100644 --- a/mrdocs.rnc +++ b/mrdocs.rnc @@ -294,7 +294,7 @@ grammar attribute name { text } ?, TextNode * } - TextNode = ( Link | Styled | Text ) + TextNode = ( Link | Styled | Text | Reference | Copied ) Link = element link { attribute href { text }, text } Styled = ( @@ -303,6 +303,16 @@ grammar element bold { text } | element italic { text } ) Text = element text { text } + Reference = element reference { ID ?, text } + Copied = + element ( + copydoc | + copybrief | + copydetails + ) + { + ID ?, text + } #--------------------------------------------- diff --git a/src/lib/-HTML/HTMLCorpus.cpp b/src/lib/-HTML/HTMLCorpus.cpp index 0ff371b06..68c3d868b 100644 --- a/src/lib/-HTML/HTMLCorpus.cpp +++ b/src/lib/-HTML/HTMLCorpus.cpp @@ -262,12 +262,15 @@ measureLeftMargin( class DomJavadoc : public dom::LazyObjectImpl { + const HTMLCorpus& corpus_; Javadoc const& jd_; public: DomJavadoc( + const HTMLCorpus& corpus, Javadoc const& jd) noexcept - : jd_(jd) + : corpus_(corpus) + , jd_(jd) { } @@ -308,7 +311,7 @@ class DomJavadoc : public dom::LazyObjectImpl storage_type list; list.reserve(2); - auto ov = jd_.makeOverview(); + auto ov = jd_.makeOverview(corpus_.getCorpus()); // brief if(ov.brief) @@ -330,7 +333,7 @@ HTMLCorpus:: getJavadoc( Javadoc const& jd) const { - return dom::newObject(jd); + return dom::newObject(*this, jd); } } // html diff --git a/src/lib/-XML/XMLWriter.cpp b/src/lib/-XML/XMLWriter.cpp index f74495af4..bbf65e1c6 100644 --- a/src/lib/-XML/XMLWriter.cpp +++ b/src/lib/-XML/XMLWriter.cpp @@ -593,12 +593,53 @@ writeNode( case doc::Kind::returns: writeReturns(static_cast(node)); break; + case doc::Kind::reference: + writeReference(static_cast(node)); + break; + case doc::Kind::copied: + writeCopied(static_cast(node)); + break; default: // unknown kind MRDOCS_UNREACHABLE(); } } +void +XMLWriter:: +writeReference( + doc::Reference const& node) +{ + tags_.write("reference", node.string, { + { node.id } + }); +} + +void +XMLWriter:: +writeCopied( + doc::Copied const& node) +{ + std::string_view tag_name; + switch(node.parts) + { + case doc::Parts::all: + tag_name = "copydoc"; + break; + case doc::Parts::brief: + tag_name = "copybrief"; + break; + case doc::Parts::description: + tag_name = "copydetails"; + break; + default: + MRDOCS_UNREACHABLE(); + } + tags_.write(tag_name, node.string, { + { node.id } + }); +} + void XMLWriter:: writeLink( diff --git a/src/lib/-XML/XMLWriter.hpp b/src/lib/-XML/XMLWriter.hpp index bd830f4df..dec15d889 100644 --- a/src/lib/-XML/XMLWriter.hpp +++ b/src/lib/-XML/XMLWriter.hpp @@ -91,6 +91,8 @@ class XMLWriter void writeStyledText(doc::Styled const& node); void writeText(doc::Text const& node); void writeTParam(doc::TParam const& node); + void writeReference(doc::Reference const& node); + void writeCopied(doc::Copied const& node); }; } // xml diff --git a/src/lib/-adoc/AdocCorpus.cpp b/src/lib/-adoc/AdocCorpus.cpp index 8bcab0aa1..e8090ea60 100644 --- a/src/lib/-adoc/AdocCorpus.cpp +++ b/src/lib/-adoc/AdocCorpus.cpp @@ -26,11 +26,19 @@ namespace { class DocVisitor { + const AdocCorpus& corpus_; std::string& dest_; std::back_insert_iterator ins_; public: - explicit DocVisitor(std::string& dest) noexcept; + DocVisitor( + const AdocCorpus& corpus, + std::string& dest) noexcept + : corpus_(corpus) + , dest_(dest) + , ins_(std::back_inserter(dest_)) + { + } void operator()(doc::Admonition const& I); void operator()(doc::Code const& I); @@ -43,19 +51,12 @@ class DocVisitor void operator()(doc::Text const& I); void operator()(doc::Styled const& I); void operator()(doc::TParam const& I); + void operator()(doc::Reference const& I); std::size_t measureLeftMargin( doc::List const& list); }; -DocVisitor:: -DocVisitor( - std::string& dest) noexcept - : dest_(dest) - , ins_(std::back_inserter(dest_)) -{ -} - void DocVisitor:: operator()( @@ -124,7 +125,7 @@ DocVisitor:: operator()( doc::Heading const& I) { - fmt::format_to(ins_, "=== {}\n", I.string); + fmt::format_to(ins_, "\n=== {}\n", I.string); } // Also handles doc::Brief @@ -148,7 +149,7 @@ operator()( } } dest_.push_back('\n'); - dest_.push_back('\n'); + // dest_.push_back('\n'); } void @@ -168,7 +169,7 @@ DocVisitor:: operator()( doc::ListItem const& I) { - dest_.append("* "); + dest_.append("\n* "); for(auto const& it : RangeFor(I.children)) { auto const n = dest_.size(); @@ -243,6 +244,17 @@ operator()(doc::TParam const& I) //dest_ += I.string; } +void +DocVisitor:: +operator()(doc::Reference const& I) +{ + //dest_ += I.string; + if(I.id == SymbolID::zero) + return (*this)(static_cast(I)); + fmt::format_to(std::back_inserter(dest_), "xref:{}[{}]", + corpus_.getXref(I.id), I.string); +} + std::size_t DocVisitor:: measureLeftMargin( @@ -267,12 +279,15 @@ measureLeftMargin( class DomJavadoc : public dom::LazyObjectImpl { + const AdocCorpus& corpus_; Javadoc const& jd_; public: DomJavadoc( + const AdocCorpus& corpus, Javadoc const& jd) noexcept - : jd_(jd) + : corpus_(corpus) + , jd_(jd) { } @@ -284,7 +299,7 @@ class DomJavadoc : public dom::LazyObjectImpl T const& I) const { std::string s; - DocVisitor visitor(s); + DocVisitor visitor(corpus_, s); doc::visit(I, visitor); if(! s.empty()) list.emplace_back(key, std::move(s)); @@ -298,7 +313,7 @@ class DomJavadoc : public dom::LazyObjectImpl std::vector const& nodes) const { std::string s; - DocVisitor visitor(s); + DocVisitor visitor(corpus_, s); for(auto const& t : nodes) doc::visit(*t, visitor); if(! s.empty()) @@ -313,7 +328,7 @@ class DomJavadoc : public dom::LazyObjectImpl storage_type list; list.reserve(2); - auto ov = jd_.makeOverview(); + auto ov = jd_.makeOverview(corpus_.getCorpus()); // brief if(ov.brief) @@ -381,7 +396,7 @@ AdocCorpus:: getJavadoc( Javadoc const& jd) const { - return dom::newObject(jd); + return dom::newObject(*this, jd); } } // adoc diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index d1d9dcc7b..396082967 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -1543,9 +1543,26 @@ class ASTVisitor { // VFALCO investigate whether we can use // ASTContext::getCommentForDecl instead + #if 1 RawComment* RC = D->getASTContext().getRawCommentForDeclNoCache(D); - parseJavadoc(javadoc, RC, D, config_, diags_); + if(! RC) + return; + comments::FullComment* FC = + RC->parse(D->getASTContext(), &sema_.getPreprocessor(), D); + #else + comments::FullComment* FC = + D->getASTContext().getCommentForDecl( + D, &sema_.getPreprocessor()); + #endif + // KRYSTIAN FIXME: clang ignores documentation comments + // when there is a preprocessor directive between the end + // of the comment and the declaration location. there are two + // ways to fix this: either set the declaration begin location + // to be before and preprocessor directives, or submit a patch + // which disables this behavior (it's not entirely clear why + // this check occurs anyways, so some investigation is needed) + parseJavadoc(javadoc, FC, D, config_, diags_); } //------------------------------------------------ diff --git a/src/lib/AST/AnyBlock.hpp b/src/lib/AST/AnyBlock.hpp index 5387cf712..bd64aeacd 100644 --- a/src/lib/AST/AnyBlock.hpp +++ b/src/lib/AST/AnyBlock.hpp @@ -261,14 +261,16 @@ class JavadocNodesBlock : public BitcodeReader::AnyBlock { BitcodeReader& br_; + Javadoc& jd_; public: doc::List nodes; - explicit JavadocNodesBlock( - BitcodeReader& br) noexcept + BitcodeReader& br, + Javadoc& jd) noexcept : br_(br) + , jd_(jd) { } @@ -292,6 +294,31 @@ class JavadocNodesBlock return Error::success(); } + case JAVADOC_NODE_PART: + { + doc::Parts parts = doc::Parts::all; + if(auto err = decodeRecord(R, parts, Blob)) + return err; + auto node = nodes.back().get(); + if(node->kind != doc::Kind::copied) + return formatError("part on wrong kind"); + static_cast(node)->parts = parts; + return Error::success(); + } + + case JAVADOC_NODE_SYMBOLREF: + { + SymbolID id; + if(auto err = decodeRecord(R, id, Blob)) + return err; + auto node = nodes.back().get(); + if(node->kind != doc::Kind::reference && + node->kind != doc::Kind::copied) + return formatError("reference on wrong kind"); + static_cast(node)->id = id; + return Error::success(); + } + case JAVADOC_PARAM_DIRECTION: { doc::ParamDirection direction = @@ -324,19 +351,54 @@ class JavadocNodesBlock doc::Kind kind{}; if(auto err = decodeRecord(R, kind, Blob)) return err; - return visit(kind, - [&]() - { - if constexpr(! std::is_same_v) - { - nodes.emplace_back(std::make_unique()); - return Error::success(); - } - else - { - return formatError("unknown doc::Kind"); - } - }); + switch(kind) + { + case doc::Kind::admonition: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::brief: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::code: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::heading: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::paragraph: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::link: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::reference: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::copied: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::list_item: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::param: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::returns: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::styled: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::text: + nodes.emplace_back(std::make_unique()); + break; + case doc::Kind::tparam: + nodes.emplace_back(std::make_unique()); + break; + default: + return formatError("unknown doc::Kind"); + } + return Error::success(); } case JAVADOC_NODE_STRING: @@ -350,6 +412,8 @@ class JavadocNodesBlock case doc::Kind::text: case doc::Kind::styled: case doc::Kind::link: + case doc::Kind::reference: + case doc::Kind::copied: static_cast( node)->string = Blob.str(); return Error::success(); @@ -398,7 +462,7 @@ class JavadocNodesBlock node->kind == doc::Kind::styled) return formatError("text node cannot have list"); - JavadocNodesBlock B(br_); + JavadocNodesBlock B(br_, jd_); if(auto err = br_.readBlock(B, ID)) return err; static_cast(node)->append( @@ -428,7 +492,7 @@ class JavadocBlock public: JavadocBlock( std::unique_ptr& I, - BitcodeReader& br) noexcept + BitcodeReader& br) : br_(br) , I_(I) { @@ -443,7 +507,7 @@ class JavadocBlock { case BI_JAVADOC_LIST_BLOCK_ID: { - JavadocNodesBlock B(br_); + JavadocNodesBlock B(br_, *I_); if(auto err = br_.readBlock(B, ID)) return err; I_->append(std::move(B.nodes)); diff --git a/src/lib/AST/BitcodeIDs.hpp b/src/lib/AST/BitcodeIDs.hpp index 402c3c509..5f488238e 100644 --- a/src/lib/AST/BitcodeIDs.hpp +++ b/src/lib/AST/BitcodeIDs.hpp @@ -117,6 +117,8 @@ enum RecordID JAVADOC_NODE_KIND, JAVADOC_NODE_STRING, JAVADOC_NODE_STYLE, + JAVADOC_NODE_PART, + JAVADOC_NODE_SYMBOLREF, JAVADOC_PARAM_DIRECTION, ENUM_SCOPED, ENUM_VALUE_NAME, diff --git a/src/lib/AST/BitcodeWriter.cpp b/src/lib/AST/BitcodeWriter.cpp index 58bf2aff1..9e960ee8e 100644 --- a/src/lib/AST/BitcodeWriter.cpp +++ b/src/lib/AST/BitcodeWriter.cpp @@ -271,6 +271,8 @@ RecordIDNameMap = []() {JAVADOC_NODE_KIND, {"JavadocNodeKind", &Integer32Abbrev}}, {JAVADOC_NODE_STRING, {"JavadocNodeString", &StringAbbrev}}, {JAVADOC_NODE_STYLE, {"JavadocNodeStyle", &Integer32Abbrev}}, + {JAVADOC_NODE_PART, {"JavadocNodePart", &Integer32Abbrev}}, + {JAVADOC_NODE_SYMBOLREF, {"JavadocNodeSymbol", &SymbolIDAbbrev}}, {JAVADOC_PARAM_DIRECTION, {"JavadocParamDirection", &Integer32Abbrev}}, {NAMESPACE_MEMBERS, {"NamespaceMembers", &SymbolIDsAbbrev}}, {NAMESPACE_SPECIALIZATIONS, {"NamespaceSpecializations", &SymbolIDsAbbrev}}, @@ -358,8 +360,9 @@ RecordsByBlock{ {}}, // doc::Node {BI_JAVADOC_NODE_BLOCK_ID, - {JAVADOC_NODE_KIND, JAVADOC_NODE_HREF, JAVADOC_NODE_STRING, JAVADOC_NODE_STYLE, - JAVADOC_NODE_ADMONISH, JAVADOC_PARAM_DIRECTION}}, + {JAVADOC_NODE_KIND, JAVADOC_NODE_HREF, JAVADOC_NODE_STRING, + JAVADOC_NODE_STYLE, JAVADOC_NODE_ADMONISH, JAVADOC_PARAM_DIRECTION, + JAVADOC_NODE_PART, JAVADOC_NODE_SYMBOLREF}}, // NamespaceInfo {BI_NAMESPACE_BLOCK_ID, {NAMESPACE_MEMBERS, NAMESPACE_SPECIALIZATIONS, NAMESPACE_BITS}}, @@ -874,31 +877,26 @@ emitBlock( StreamSubBlockGuard Block( Stream, BI_JAVADOC_NODE_BLOCK_ID); emitRecord(I.kind, JAVADOC_NODE_KIND); - doc::visit(I.kind, - [&]() + visit(I, [&](const NodeTy& J) { - if constexpr(! std::is_void_v) - { - auto const& J = static_cast(I); - if constexpr(requires { J.href; }) - emitRecord(J.href, JAVADOC_NODE_HREF); - if constexpr(requires { J.string; }) - emitRecord(J.string, JAVADOC_NODE_STRING); - if constexpr(requires { J.style; }) - emitRecord(J.style, JAVADOC_NODE_STYLE); - if constexpr(requires { J.admonish; }) - emitRecord(J.admonish, JAVADOC_NODE_ADMONISH); - if constexpr(requires { J.direction; }) - emitRecord(J.direction, JAVADOC_PARAM_DIRECTION); - if constexpr(requires { J.name; }) - emitRecord(J.name, JAVADOC_NODE_STRING); - if constexpr(requires { J.children; }) - emitBlock(J.children); - } - else - { - MRDOCS_UNREACHABLE(); - } + if constexpr(requires { J.href; }) + emitRecord(J.href, JAVADOC_NODE_HREF); + if constexpr(requires { J.string; }) + emitRecord(J.string, JAVADOC_NODE_STRING); + if constexpr(requires { J.style; }) + emitRecord(J.style, JAVADOC_NODE_STYLE); + if constexpr(requires { J.admonish; }) + emitRecord(J.admonish, JAVADOC_NODE_ADMONISH); + if constexpr(requires { J.direction; }) + emitRecord(J.direction, JAVADOC_PARAM_DIRECTION); + if constexpr(requires { J.parts; }) + emitRecord(J.parts, JAVADOC_NODE_PART); + if constexpr(requires { J.id; }) + emitRecord(J.id, JAVADOC_NODE_SYMBOLREF); + if constexpr(requires { J.name; }) + emitRecord(J.name, JAVADOC_NODE_STRING); + if constexpr(requires { J.children; }) + emitBlock(J.children); }); } @@ -944,42 +942,42 @@ emitBlock( emitRecord(TI->Kind, TYPEINFO_KIND); emitRecord(TI->IsPackExpansion, TYPEINFO_IS_PACK); visit(*TI, [&](const T& t) + { + if constexpr(requires { t.id; }) + emitRecord(t.id, TYPEINFO_ID); + if constexpr(requires { t.Name; }) + emitRecord(t.Name, TYPEINFO_NAME); + if constexpr(requires { t.CVQualifiers; }) + emitRecord(t.CVQualifiers, TYPEINFO_CVQUAL); + + if constexpr(T::isSpecialization()) { - if constexpr(requires { t.id; }) - emitRecord(t.id, TYPEINFO_ID); - if constexpr(requires { t.Name; }) - emitRecord(t.Name, TYPEINFO_NAME); - if constexpr(requires { t.CVQualifiers; }) - emitRecord(t.CVQualifiers, TYPEINFO_CVQUAL); - - if constexpr(T::isSpecialization()) - { - for(const auto& targ : t.TemplateArgs) - emitBlock(targ); - } - - if constexpr(requires { t.ParentType; }) - emitBlock(t.ParentType, BI_TYPEINFO_PARENT_BLOCK_ID); - - if constexpr(requires { t.PointeeType; }) - emitBlock(t.PointeeType, BI_TYPEINFO_CHILD_BLOCK_ID); - - if constexpr(T::isArray()) - { - emitBlock(t.ElementType, BI_TYPEINFO_CHILD_BLOCK_ID); - emitBlock(t.Bounds); - } - - if constexpr(T::isFunction()) - { - emitBlock(t.ReturnType, BI_TYPEINFO_CHILD_BLOCK_ID); - for(const auto& P : t.ParamTypes) - emitBlock(P, BI_TYPEINFO_PARAM_BLOCK_ID); - - emitRecord(t.RefQualifier, TYPEINFO_REFQUAL); - emitRecord(t.ExceptionSpec, TYPEINFO_EXCEPTION_SPEC); - } - }); + for(const auto& targ : t.TemplateArgs) + emitBlock(targ); + } + + if constexpr(requires { t.ParentType; }) + emitBlock(t.ParentType, BI_TYPEINFO_PARENT_BLOCK_ID); + + if constexpr(requires { t.PointeeType; }) + emitBlock(t.PointeeType, BI_TYPEINFO_CHILD_BLOCK_ID); + + if constexpr(T::isArray()) + { + emitBlock(t.ElementType, BI_TYPEINFO_CHILD_BLOCK_ID); + emitBlock(t.Bounds); + } + + if constexpr(T::isFunction()) + { + emitBlock(t.ReturnType, BI_TYPEINFO_CHILD_BLOCK_ID); + for(const auto& P : t.ParamTypes) + emitBlock(P, BI_TYPEINFO_PARAM_BLOCK_ID); + + emitRecord(t.RefQualifier, TYPEINFO_REFQUAL); + emitRecord(t.ExceptionSpec, TYPEINFO_EXCEPTION_SPEC); + } + }); } void @@ -1055,28 +1053,28 @@ emitBlock( { StreamSubBlockGuard Block(Stream, BI_TEMPLATE_PARAM_BLOCK_ID); visit(*T, [&](const T& P) + { + emitRecord(P.Kind, TEMPLATE_PARAM_KIND); + emitRecord(P.Name, TEMPLATE_PARAM_NAME); + emitRecord(P.IsParameterPack, TEMPLATE_PARAM_IS_PACK); + + if(P.Default) + emitBlock(P.Default); + + if constexpr(T::isType()) { - emitRecord(P.Kind, TEMPLATE_PARAM_KIND); - emitRecord(P.Name, TEMPLATE_PARAM_NAME); - emitRecord(P.IsParameterPack, TEMPLATE_PARAM_IS_PACK); - - if(P.Default) - emitBlock(P.Default); - - if constexpr(T::isType()) - { - emitRecord(P.KeyKind, TEMPLATE_PARAM_KEY_KIND); - } - if constexpr(T::isNonType()) - { - emitBlock(P.Type); - } - if constexpr(T::isTemplate()) - { - for(const auto& P : P.Params) - emitBlock(P); - } - }); + emitRecord(P.KeyKind, TEMPLATE_PARAM_KEY_KIND); + } + if constexpr(T::isNonType()) + { + emitBlock(P.Type); + } + if constexpr(T::isTemplate()) + { + for(const auto& P : P.Params) + emitBlock(P); + } + }); } void @@ -1086,24 +1084,24 @@ emitBlock( { StreamSubBlockGuard Block(Stream, BI_TEMPLATE_ARG_BLOCK_ID); visit(*T, [&](const T& A) + { + emitRecord(A.Kind, TEMPLATE_ARG_KIND); + emitRecord(A.IsPackExpansion, TEMPLATE_ARG_IS_PACK); + + if constexpr(T::isType()) { - emitRecord(A.Kind, TEMPLATE_ARG_KIND); - emitRecord(A.IsPackExpansion, TEMPLATE_ARG_IS_PACK); - - if constexpr(T::isType()) - { - emitBlock(A.Type); - } - else if constexpr(T::isNonType()) - { - emitBlock(A.Value); - } - else if constexpr(T::isTemplate()) - { - emitRecord(A.Template, TEMPLATE_ARG_TEMPLATE); - emitRecord(A.Name, TEMPLATE_ARG_NAME); - } - }); + emitBlock(A.Type); + } + else if constexpr(T::isNonType()) + { + emitBlock(A.Value); + } + else if constexpr(T::isTemplate()) + { + emitRecord(A.Template, TEMPLATE_ARG_TEMPLATE); + emitRecord(A.Name, TEMPLATE_ARG_NAME); + } + }); } void diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp index 00abf1940..bcfc92e74 100644 --- a/src/lib/AST/ParseJavadoc.cpp +++ b/src/lib/AST/ParseJavadoc.cpp @@ -5,6 +5,7 @@ // 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 // @@ -115,10 +116,38 @@ class JavadocVisitor static std::string& ensureUTF8(std::string&&); void visitChildren(Comment const* C); + + class [[nodiscard]] BlockScope + { + JavadocVisitor& visitor_; + doc::Block* prev_; + + public: + BlockScope( + JavadocVisitor& visitor, + doc::Block* blk) + : visitor_(visitor) + , prev_(visitor.block_) + { + visitor_.block_ = blk; + } + + ~BlockScope() + { + visitor_.block_ = prev_; + } + }; + + BlockScope enterScope(doc::Block& blk) + { + return BlockScope(*this, &blk); + } + public: JavadocVisitor( - RawComment const*, Decl const*, + FullComment const*, Decl const*, Config const&, Diagnostics&); + Javadoc build(); void visitComment(Comment const* C); @@ -144,33 +173,6 @@ class JavadocVisitor //------------------------------------------------ -template -struct Scope -{ - Scope( - U& u, - T*& t0) noexcept - : t0_(t0) - , pt_(t0) - { - pt_ = &u; - } - - ~Scope() - { - pt_ = t0_; - } - -private: - T* t0_; - T*& pt_; -}; - -template -Scope(U&, T*&) -> Scope; - -//------------------------------------------------ - std::string& JavadocVisitor:: ensureUTF8( @@ -203,14 +205,14 @@ visitChildren( JavadocVisitor:: JavadocVisitor( - RawComment const* RC, + FullComment const* FC, Decl const* D, Config const& config, Diagnostics& diags) : config_(config) , ctx_(D->getASTContext()) , sm_(ctx_.getSourceManager()) - , FC_(RC->parse(D->getASTContext(), nullptr, D)) + , FC_(FC) , diags_(diags) { } @@ -326,6 +328,61 @@ visitHTMLEndTagComment( --htmlTagNesting_; } +static +doc::Parts +convertCopydoc(unsigned id) +{ + switch(id) + { + case CommandTraits::KCI_copydoc: + return doc::Parts::all; + case CommandTraits::KCI_copybrief: + return doc::Parts::brief; + case CommandTraits::KCI_copydetails: + return doc::Parts::description; + default: + MRDOCS_UNREACHABLE(); + } +} + +static +doc::Style +convertStyle(InlineCommandComment::RenderKind kind) +{ + switch(kind) + { + case InlineCommandComment::RenderKind::RenderMonospaced: + return doc::Style::mono; + case InlineCommandComment::RenderKind::RenderBold: + return doc::Style::bold; + case InlineCommandComment::RenderKind::RenderEmphasized: + return doc::Style::italic; + case InlineCommandComment::RenderKind::RenderNormal: + case InlineCommandComment::RenderKind::RenderAnchor: + return doc::Style::none; + default: + // unknown RenderKind + MRDOCS_UNREACHABLE(); + } +} + +static +doc::ParamDirection +convertDirection(ParamCommandComment::PassDirection kind) +{ + switch(kind) + { + case ParamCommandComment::PassDirection::In: + return doc::ParamDirection::in; + case ParamCommandComment::PassDirection::Out: + return doc::ParamDirection::out; + case ParamCommandComment::PassDirection::InOut: + return doc::ParamDirection::inout; + default: + MRDOCS_UNREACHABLE(); + } +} + void JavadocVisitor:: visitInlineCommandComment( @@ -338,7 +395,10 @@ visitInlineCommandComment( // VFALCO I'd like to know when this happens MRDOCS_ASSERT(cmd != nullptr); - switch(cmd->getID()) + // KRYSTIAN FIXME: the text for a copydoc/ref command + // should not include illegal characters + // (e.g. periods that occur after the symbol name) + switch(unsigned ID = cmd->getID()) { // Emphasis case CommandTraits::KCI_a: @@ -348,8 +408,8 @@ visitInlineCommandComment( if(! goodArgCount(1, *C)) return; auto style = doc::Style::italic; - auto s = C->getArgText(0); - block_->emplace_back(doc::Styled(s.str(), style)); + block_->emplace_back(doc::Styled( + C->getArgText(0).str(), style)); return; } @@ -358,37 +418,27 @@ visitInlineCommandComment( case CommandTraits::KCI_copydetails: case CommandTraits::KCI_copydoc: { - if(C->getNumArgs() != 1) - { - // report an error - diags_.error("getNumArgs() != 1"); + if(! goodArgCount(1, *C)) return; - } + // the referenced symbol will be resolved during + // the finalization step once all symbol are extracted + block_->emplace_back(doc::Copied( + C->getArgText(0).str(), convertCopydoc(ID))); return; } - - default: - break; + case CommandTraits::KCI_ref: + { + if(! goodArgCount(1, *C)) + return; + // the referenced symbol will be resolved during + // the finalization step once all symbol are extracted + block_->emplace_back(doc::Reference( + C->getArgText(0).str())); + return; } - doc::Style style(doc::Style::none); - switch (C->getRenderKind()) - { - case InlineCommandComment::RenderKind::RenderMonospaced: - style = doc::Style::mono; - break; - case InlineCommandComment::RenderKind::RenderBold: - style = doc::Style::bold; - break; - case InlineCommandComment::RenderKind::RenderEmphasized: - style = doc::Style::italic; - break; - case InlineCommandComment::RenderKind::RenderNormal: - case InlineCommandComment::RenderKind::RenderAnchor: - break; default: - // unknown RenderKind - MRDOCS_UNREACHABLE(); + break; } // It looks like the clang parser does not @@ -403,6 +453,7 @@ visitInlineCommandComment( for(unsigned i = 0; i < C->getNumArgs(); ++i) s.append(C->getArgText(i)); + doc::Style style = convertStyle(C->getRenderKind()); if(style != doc::Style::none) block_->emplace_back(doc::Styled(std::move(s), style)); else @@ -423,7 +474,7 @@ visitParagraphComment( if(block_) return visitChildren(C); doc::Paragraph paragraph; - Scope scope(paragraph, block_); + auto scope = enterScope(paragraph); visitChildren(C); // VFALCO Figure out why we get empty ParagraphComment if(! paragraph.empty()) @@ -451,7 +502,8 @@ visitBlockCommandComment( case CommandTraits::KCI_short: { doc::Brief brief; - Scope scope(brief, block_); + auto scope = enterScope(brief); + // Scope scope(brief, block_); visitChildren(C->getParagraph()); // Here, we want empty briefs, because // the @brief command was explicitly given. @@ -463,7 +515,8 @@ visitBlockCommandComment( case CommandTraits::KCI_returns: { doc::Returns returns; - Scope scope(returns, block_); + auto scope = enterScope(returns); + // Scope scope(returns, block_); visitChildren(C->getParagraph()); jd_.emplace_back(std::move(returns)); return; @@ -684,7 +737,7 @@ visitBlockCommandComment( if(cmd->getID() == CommandTraits::KCI_note) { doc::Admonition paragraph(doc::Admonish::note); - Scope scope(paragraph, block_); + auto scope = enterScope(paragraph); visitChildren(C->getParagraph()); jd_.emplace_back(std::move(paragraph)); return; @@ -692,7 +745,7 @@ visitBlockCommandComment( if(cmd->getID() == CommandTraits::KCI_warning) { doc::Admonition paragraph(doc::Admonish::warning); - Scope scope(paragraph, block_); + auto scope = enterScope(paragraph); visitChildren(C->getParagraph()); jd_.emplace_back(std::move(paragraph)); return; @@ -703,7 +756,7 @@ visitBlockCommandComment( // for Boost libraries using @par as a // section heading. doc::Paragraph paragraph; - Scope scope(paragraph, block_); + auto scope = enterScope(paragraph); visitChildren(C->getParagraph()); if(! paragraph.children.empty()) { @@ -731,7 +784,7 @@ visitBlockCommandComment( if(cmd->getID() == CommandTraits::KCI_li) { doc::ListItem paragraph; - Scope scope(paragraph, block_); + auto scope = enterScope(paragraph); visitChildren(C->getParagraph()); jd_.emplace_back(std::move(paragraph)); return; @@ -755,21 +808,9 @@ visitParamCommandComment( param.name = "@anon"; } if(C->isDirectionExplicit()) - { - switch(C->getDirection()) - { - case ParamCommandComment::PassDirection::In: - param.direction = doc::ParamDirection::in; - break; - case ParamCommandComment::PassDirection::Out: - param.direction = doc::ParamDirection::out; - break; - case ParamCommandComment::PassDirection::InOut: - param.direction = doc::ParamDirection::inout; - break; - } - } - Scope scope(param, block_); + param.direction = convertDirection(C->getDirection()); + + auto scope = enterScope(param); visitChildren(C->getParagraph()); // We want the node even if it is empty jd_.emplace_back(std::move(param)); @@ -791,7 +832,7 @@ visitTParamCommandComment( diags_.error("Missing parameter name in @tparam"); tparam.name = "@anon"; } - Scope scope(tparam, block_); + auto scope = enterScope(tparam); visitChildren(C->getParagraph()); // We want the node even if it is empty jd_.emplace_back(std::move(tparam)); @@ -803,7 +844,7 @@ visitVerbatimBlockComment( VerbatimBlockComment const* C) { doc::Code code; - Scope scope(code, block_); + auto scope = enterScope(code); //if(C->hasNonWhitespaceParagraph()) visitChildren(C); jd_.emplace_back(std::move(code)); @@ -874,15 +915,14 @@ initCustomCommentCommands(ASTContext& context) void parseJavadoc( std::unique_ptr& jd, - RawComment* RC, + FullComment* FC, Decl const* D, Config const& config, Diagnostics& diags) { - if(! RC) + if(! FC) return; - RC->setAttached(); - auto result = JavadocVisitor(RC, D, config, diags).build(); + auto result = JavadocVisitor(FC, D, config, diags).build(); if(jd == nullptr) { // Do not create javadocs which have no nodes diff --git a/src/lib/AST/ParseJavadoc.hpp b/src/lib/AST/ParseJavadoc.hpp index b0c08babc..ad3ac70c9 100644 --- a/src/lib/AST/ParseJavadoc.hpp +++ b/src/lib/AST/ParseJavadoc.hpp @@ -23,6 +23,10 @@ class Decl; class ASTContext; class RawComment; +namespace comments { + class FullComment; +} // comments + namespace mrdocs { /** Initialize clang to recognize our custom comments. @@ -39,7 +43,7 @@ initCustomCommentCommands( void parseJavadoc( std::unique_ptr& jd, - RawComment* RC, + comments::FullComment* FC, Decl const* D, Config const& config, Diagnostics& diags); diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index 0fa4aae96..1b0010c1a 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -12,6 +12,8 @@ #include "CorpusImpl.hpp" #include "lib/AST/ASTVisitor.hpp" +#include "lib/Metadata/Finalize.hpp" +#include "lib/Lib/Lookup.hpp" #include "lib/Support/Error.hpp" #include #include @@ -34,10 +36,13 @@ begin() const noexcept -> [](const Corpus* corpus, const Info* val) -> const Info* { + MRDOCS_ASSERT(val); const CorpusImpl* impl = static_cast(corpus); auto it = impl->info_.find(val->id); - return (++it)->get(); + if(++it == impl->info_.end()) + return nullptr; + return it->get(); }); } @@ -214,6 +219,9 @@ build( format_duration(clock_type::now() - start_time)); #endif + auto lookup = std::make_unique(*corpus); + + finalize(corpus->info_, *lookup); return corpus; } diff --git a/src/lib/Lib/ExecutionContext.cpp b/src/lib/Lib/ExecutionContext.cpp index e9bcb76eb..eb5b92b7e 100644 --- a/src/lib/Lib/ExecutionContext.cpp +++ b/src/lib/Lib/ExecutionContext.cpp @@ -13,7 +13,6 @@ #include "ExecutionContext.hpp" #include "lib/AST/Bitcode.hpp" #include "lib/Metadata/Reduce.hpp" -#include "lib/Metadata/Finalize.hpp" #include namespace clang { @@ -94,7 +93,6 @@ mrdocs::Expected InfoExecutionContext:: results() { - finalize(info_); return std::move(info_); } @@ -162,7 +160,6 @@ results() if(! errors.empty()) return Unexpected(errors); - finalize(result); return result; } diff --git a/src/lib/Lib/Lookup.cpp b/src/lib/Lib/Lookup.cpp new file mode 100644 index 000000000..57398edec --- /dev/null +++ b/src/lib/Lib/Lookup.cpp @@ -0,0 +1,224 @@ +// +// 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) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "Lookup.hpp" +#include + +namespace clang { +namespace mrdocs { + +namespace { + +bool supportsLookup(const Info* info) +{ + return info && + (info->isRecord() || + info->isNamespace() || + info->isSpecialization()); +} + +void +buildLookups( + const Corpus& corpus, + const Info& info, + LookupTable& lookups) +{ + visit(info, [&](const InfoTy& I) + { + if constexpr(InfoTy::isRecord() || InfoTy::isNamespace()) + { + for(const SymbolID& M : I.Members) + { + const Info* child = corpus.find(M); + // KRYSTIAN TODO: handle inline/anonymous namespaces + // KRYSTIAN TODO: injected class names? + if(child->Name.empty()) + continue; + lookups.add(child->Name, child); + } + } + }); +} + +} // (anon) + +LookupTable:: +LookupTable( + const Info& info, + const Corpus& corpus) +{ + MRDOCS_ASSERT(supportsLookup(&info)); + + buildLookups(corpus, info, *this); +} + +SymbolLookup:: +SymbolLookup(const Corpus& corpus) + : corpus_(corpus) +{ + for(const Info& I : corpus_) + { + if(! supportsLookup(&I)) + continue; + lookup_tables_.emplace(&I, LookupTable(I, corpus_)); + } +} + +const Info* +SymbolLookup:: +adjustLookupContext( + const Info* context) +{ + // find the innermost enclosing context that supports name lookup + while(! supportsLookup(context)) + { + MRDOCS_ASSERT(! context->Namespace.empty()); + const SymbolID& parent = context->Namespace.front(); + context = corpus_.find(parent); + } + return context; +} + +const Info* +SymbolLookup:: +getTypeAsTag( + const std::unique_ptr& T) +{ + if(! T) + return nullptr; + SymbolID id = visit(*T, [](const T& t) + { + if constexpr(T::isTag() || T::isSpecialization()) + return t.id; + return SymbolID::zero; + }); + if(id == SymbolID::zero) + return nullptr; + return corpus_.find(id); +} + +const Info* +SymbolLookup:: +lookThroughTypedefs(const Info* I) +{ + if(! I || ! I->isTypedef()) + return I; + auto* TI = static_cast(I); + return lookThroughTypedefs(getTypeAsTag(TI->Type)); +} + +const Info* +SymbolLookup:: +lookupInContext( + const Info* context, + std::string_view name, + bool for_nns) +{ + context = lookThroughTypedefs(context); + // KRYSTIAN FIXME: enumerators need to have their own + // info type for lookup to work + if(! context || context->isEnum()) + return nullptr; + LookupTable& table = lookup_tables_.at(context); + // KRYSTIAN FIXME: disambiguation based on signature + for(auto& result : table.lookup(name)) + { + // per [basic.lookup.qual.general] p1, when looking up a + // component name of a nested-name-specifier, we only consider: + // - namespaces, + // - types, and + // - templates whose specializations are types + if(for_nns && + (! result->isNamespace() && + ! result->isRecord() && + ! result->isEnum() && + ! result->isTypedef())) + continue; + return result; + } + + // if this is a record and nothing was found, + // search base classes for the name + if(context->isRecord()) + { + const auto* RI = static_cast< + const RecordInfo*>(context); + // KRYSTIAN FIXME: resolve ambiguities & report errors + for(const auto& B : RI->Bases) + { + if(const Info* result = lookupInContext( + getTypeAsTag(B.Type), name, for_nns)) + return result; + } + } + + return nullptr; +} + +const Info* +SymbolLookup:: +lookupUnqualifiedImpl( + const Info* context, + std::string_view name, + bool for_nns) +{ + if(! context) + return nullptr; + context = adjustLookupContext(context); + while(context) + { + if(auto result = lookupInContext(context, name, for_nns)) + return result; + if(context->Namespace.empty()) + return nullptr; + const SymbolID& parent = context->Namespace.front(); + context = corpus_.find(parent); + } + MRDOCS_UNREACHABLE(); +} + +const Info* +SymbolLookup:: +lookupUnqualified( + const Info* context, + std::string_view name) +{ + return lookupUnqualifiedImpl( + context, name, false); +} + +const Info* +SymbolLookup:: +lookupQualified( + const Info* context, + std::span qualifier, + std::string_view terminal) +{ + if(! context) + return nullptr; + if(qualifier.empty()) + return lookupInContext(context, terminal); + context = lookupUnqualifiedImpl( + context, qualifier.front(), true); + qualifier = qualifier.subspan(1); + if(! context) + return nullptr; + while(! qualifier.empty()) + { + if(! (context = lookupInContext( + context, qualifier.front())), true) + return nullptr; + qualifier = qualifier.subspan(1); + } + return lookupInContext(context, terminal); +} + +} // mrdocs +} // clang diff --git a/src/lib/Lib/Lookup.hpp b/src/lib/Lib/Lookup.hpp new file mode 100644 index 000000000..06e27fa31 --- /dev/null +++ b/src/lib/Lib/Lookup.hpp @@ -0,0 +1,98 @@ +// +// 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) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_LOOKUP_HPP +#define MRDOCS_LIB_LOOKUP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +class LookupTable +{ + std::unordered_multimap< + std::string_view, const Info*> lookups_; + +public: + LookupTable( + const Info& info, + const Corpus& corpus); + + auto lookup(std::string_view name) const + { + auto [first, last] = lookups_.equal_range(name); + return std::ranges::subrange( + first, last) | std::views::values; + } + + void add(std::string_view name, const Info* info) + { + lookups_.emplace(name, info); + } +}; + +class SymbolLookup +{ + const Corpus& corpus_; + + // maps symbol ID to its lookup table, if lookup is supported + std::unordered_map< + const Info*, + LookupTable> lookup_tables_; + + const Info* + adjustLookupContext(const Info* context); + + const Info* + lookThroughTypedefs(const Info* I); + + const Info* + getTypeAsTag( + const std::unique_ptr& T); + + const Info* + lookupInContext( + const Info* context, + std::string_view name, + bool for_nns = false); + + const Info* + lookupUnqualifiedImpl( + const Info* context, + std::string_view name, + bool for_nns); +public: + SymbolLookup(const Corpus& corpus); + + const Info* + lookupUnqualified( + const Info* context, + std::string_view name); + + const Info* + lookupQualified( + const Info* context, + std::span qualifier, + std::string_view terminal); +}; + +} // mrdocs +} // clang + +#endif diff --git a/src/lib/Metadata/Finalize.cpp b/src/lib/Metadata/Finalize.cpp index aff7c72cc..c2462d9a5 100644 --- a/src/lib/Metadata/Finalize.cpp +++ b/src/lib/Metadata/Finalize.cpp @@ -10,8 +10,10 @@ #include "Finalize.hpp" #include "lib/Lib/Info.hpp" +#include "lib/Support/NameParser.hpp" #include #include +#include namespace clang { namespace mrdocs { @@ -26,6 +28,52 @@ namespace mrdocs { class Finalizer { 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; + + 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::zero)); + context = info_.find(SymbolID::zero)->get(); + } + found = lookup_.lookupQualified( + context, qualifier, parse_result->name); + } + else + { + found = lookup_.lookupUnqualified( + current_, parse_result->name); + } + + // 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) { @@ -107,13 +155,35 @@ class Finalizer }); } + void finalize(doc::Node& node) + { + visit(node, [&](NodeTy& N) + { + if constexpr(requires { N.children; }) + finalize(N.children); + + if constexpr(std::derived_from) + { + if(! resolveReference(N)) + { + report::warn("Failed to resolve reference to '{}' from '{}'", + N.string, current_->Name); + } + } + }); + } + + void finalize(Javadoc& javadoc) + { + finalize(javadoc.getBlocks()); + } + template void finalize(Optional& val) requires requires { this->finalize(*val); } { - if(! val) - return; - finalize(*val); + if(val) + finalize(*val); } template @@ -122,18 +192,16 @@ class Finalizer // name unless part of a class member access... requires { this->finalize(*ptr); } { - if(! ptr) - return; - finalize(*ptr); + if(ptr) + finalize(*ptr); } template void finalize(std::unique_ptr& ptr) requires requires { this->finalize(*ptr); } { - if(! ptr) - return; - finalize(*ptr); + if(ptr) + finalize(*ptr); } template @@ -162,15 +230,25 @@ class Finalizer public: - Finalizer(InfoSet& IS) - : info_(IS) + Finalizer( + InfoSet& Info, + SymbolLookup& Lookup) + : info_(Info) + , lookup_(Lookup) { } + void finalize(Info& I) + { + current_ = &I; + visit(I, *this); + } + void operator()(NamespaceInfo& I) { check(I.Namespace); check(I.Members); + finalize(I.javadoc); finalize(I.Specializations); } @@ -178,16 +256,18 @@ class Finalizer { check(I.Namespace); check(I.Members); + finalize(I.javadoc); finalize(I.Specializations); finalize(I.Template); finalize(I.Bases); - finalize(I.Friends); + // finalize(I.Friends); } void operator()(SpecializationInfo& I) { check(I.Namespace); finalize(I.Members); + finalize(I.javadoc); finalize(I.Primary); finalize(I.Args); } @@ -195,6 +275,7 @@ class Finalizer void operator()(FunctionInfo& I) { check(I.Namespace); + finalize(I.javadoc); finalize(I.Template); finalize(I.ReturnType); finalize(I.Params); @@ -203,6 +284,7 @@ class Finalizer void operator()(TypedefInfo& I) { check(I.Namespace); + finalize(I.javadoc); finalize(I.Template); finalize(I.Type); } @@ -210,28 +292,34 @@ class Finalizer void operator()(EnumInfo& I) { check(I.Namespace); + finalize(I.javadoc); finalize(I.UnderlyingType); } void operator()(FieldInfo& I) { check(I.Namespace); + finalize(I.javadoc); finalize(I.Type); } void operator()(VariableInfo& I) { check(I.Namespace); + finalize(I.javadoc); finalize(I.Template); finalize(I.Type); } }; -void finalize(InfoSet& IS) +void finalize(InfoSet& Info, SymbolLookup& Lookup) { - Finalizer visitor(IS); - for(auto& I : IS) - visit(*I, visitor); + Finalizer visitor(Info, Lookup); + for(auto& I : Info) + { + MRDOCS_ASSERT(I); + visitor.finalize(*I); + } } } // mrdocs diff --git a/src/lib/Metadata/Finalize.hpp b/src/lib/Metadata/Finalize.hpp index ccd9a4be1..9d53590b5 100644 --- a/src/lib/Metadata/Finalize.hpp +++ b/src/lib/Metadata/Finalize.hpp @@ -12,11 +12,12 @@ #define MRDOCS_LIB_METADATA_FINALIZE_HPP #include "lib/Lib/Info.hpp" +#include "lib/Lib/Lookup.hpp" namespace clang { namespace mrdocs { -void finalize(InfoSet& Info); +void finalize(InfoSet& Info, SymbolLookup& Lookup); } // mrdocs } // clang diff --git a/src/lib/Metadata/Interface.cpp b/src/lib/Metadata/Interface.cpp index 9ed79ae21..d287f6bfe 100644 --- a/src/lib/Metadata/Interface.cpp +++ b/src/lib/Metadata/Interface.cpp @@ -103,7 +103,6 @@ class Interface::Build corpus_.get(B.Type.id)); #endif } - for(auto const& id : From.Members) { const auto& I = corpus_.get(id); diff --git a/src/lib/Metadata/Javadoc.cpp b/src/lib/Metadata/Javadoc.cpp index 33045e112..2c7f94f21 100644 --- a/src/lib/Metadata/Javadoc.cpp +++ b/src/lib/Metadata/Javadoc.cpp @@ -11,6 +11,7 @@ // #include "lib/Support/Debug.hpp" +#include #include #include #include @@ -30,6 +31,8 @@ isBlock() const noexcept case Kind::text: case Kind::link: case Kind::styled: + case Kind::reference: + case Kind::copied: return false; case Kind::admonition: @@ -57,6 +60,8 @@ isText() const noexcept case Kind::text: case Kind::link: case Kind::styled: + case Kind::reference: + case Kind::copied: return true; case Kind::admonition: @@ -75,13 +80,13 @@ isText() const noexcept } } -void +Text& Block:: emplace_back( std::unique_ptr text) { MRDOCS_ASSERT(text->isText()); - children.emplace_back(std::move(text)); + return *children.emplace_back(std::move(text)); } void @@ -97,9 +102,11 @@ append(List&& blocks) } } +#if 0 static Overview makeOverview( + Block* brief, List const& list) { doc::Overview ov; @@ -114,8 +121,8 @@ makeOverview( switch((*it)->kind) { case Kind::brief: - ov.brief = static_cast< - Paragraph const*>(it->get()); + // ov.brief = static_cast< + // Paragraph const*>(it->get()); break; case Kind::returns: ov.returns = static_cast< @@ -129,6 +136,7 @@ makeOverview( ov.tparams.push_back(static_cast< TParam const*>(it->get())); break; + #if 0 case Kind::paragraph: if(! ov.brief) ov.brief = static_cast< @@ -136,14 +144,17 @@ makeOverview( else ov.blocks.push_back(it->get()); break; + #endif default: + if(ov.brief == it->get()) + break; ov.blocks.push_back(it->get()); - break; } } return ov; } +#endif dom::String toString( @@ -197,15 +208,69 @@ Javadoc( doc::Paragraph const* Javadoc:: -brief() const noexcept +getBrief(Corpus const& corpus) const noexcept +{ + const doc::Block* brief = nullptr; + const doc::Block* promoted_brief = nullptr; + const doc::Block* copied_brief = nullptr; + for(auto const& block : blocks_) + { + if(! brief && + block->kind == doc::Kind::brief) + brief = block.get(); + if(! promoted_brief && + block->kind == doc::Kind::paragraph) + promoted_brief = block.get(); + + // if we already have an explicit/copied brief, + // don't check for additional copied briefs + if(brief || copied_brief) + continue; + + for(auto const& text : block->children) + { + if(text->kind != doc::Kind::copied) + continue; + auto* copy = static_cast(text.get()); + if(copy->id != SymbolID::zero && + (copy->parts == doc::Parts::all || + copy->parts == doc::Parts::brief)) + { + if(auto& jd = corpus.get(copy->id).javadoc) + copied_brief = jd->getBrief(corpus); + } + } + } + // an explicit brief superceeds a copied brief + if(! brief) + brief = copied_brief; + // a copied brief superceeds a promoted brief + if(! brief) + brief = promoted_brief; + return static_cast(brief); +} + +doc::List const& +Javadoc:: +getDescription(Corpus const& corpus) const noexcept { - if(brief_) - return brief_; for(auto const& block : blocks_) - if(block->kind == doc::Kind::paragraph) - return static_cast< - doc::Paragraph const*>(block.get()); - return nullptr; + { + for(auto const& text : block->children) + { + if(text->kind != doc::Kind::copied) + continue; + auto* copy = static_cast(text.get()); + if(copy->id != SymbolID::zero && + (copy->parts == doc::Parts::all || + copy->parts == doc::Parts::description)) + { + if(auto& jd = corpus.get(copy->id).javadoc) + return jd->getDescription(corpus); + } + } + } + return blocks_; } bool @@ -213,15 +278,6 @@ Javadoc:: operator==( Javadoc const& other) const noexcept { - if(! brief_ || ! other.brief_) - { - if(brief_ != other.brief_) - return false; - } - else if(! brief_->equals(*other.brief_)) - { - return false; - } return std::equal(blocks_.begin(), blocks_.end(), other.blocks_.begin(), other.blocks_.end(), [](const auto& a, const auto& b) @@ -240,9 +296,46 @@ operator!=( doc::Overview Javadoc:: -makeOverview() const +makeOverview( + const Corpus& corpus) const { - return doc::makeOverview(blocks_); + // return doc::makeOverview(blocks_); + doc::Overview ov; + ov.brief = getBrief(corpus); + + const auto& list = getDescription(corpus); + // VFALCO dupes should already be reported as + // warnings or errors by now so we don't have + // to care about it here. + + for(auto it = list.begin(); + it != list.end(); ++it) + { + MRDOCS_ASSERT(*it); + switch((*it)->kind) + { + case doc::Kind::brief: + break; + case doc::Kind::returns: + ov.returns = static_cast< + doc::Returns const*>(it->get()); + break; + case doc::Kind::param: + ov.params.push_back(static_cast< + doc::Param const*>(it->get())); + break; + case doc::Kind::tparam: + ov.tparams.push_back(static_cast< + doc::TParam const*>(it->get())); + break; + default: + if(ov.brief == it->get()) + break; + ov.blocks.push_back(it->get()); + } + } + + return ov; } std::string @@ -255,16 +348,6 @@ emplace_back( std::string result; switch(block->kind) { - case doc::Kind::brief: - { - // check for multiple briefs - if(brief_ != nullptr) - result = "multiple briefs"; - else - brief_ = static_cast< - doc::Paragraph const*>(block.get()); - break; - } case doc::Kind::param: { // check for duplicate parameter name @@ -328,11 +411,15 @@ append(doc::List&& blocks) { blocks_.reserve(blocks_.size() + blocks.size()); for(auto&& block : blocks) + emplace_back(std::unique_ptr( + static_cast(block.release()))); + #if 0 { MRDOCS_ASSERT(block->isBlock()); blocks_.emplace_back( static_cast(block.release())); } + #endif } } // mrdocs diff --git a/src/lib/Support/NameParser.cpp b/src/lib/Support/NameParser.cpp new file mode 100644 index 000000000..2abb38bdb --- /dev/null +++ b/src/lib/Support/NameParser.cpp @@ -0,0 +1,503 @@ +// +// 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) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "NameParser.hpp" +#include + +namespace clang { +namespace mrdocs { + +namespace { + +class TokenStream +{ + const char* first_; + const char* part_; + const char* ptr_; + const char* last_; + +public: + TokenStream(std::string_view str) + : first_(str.data()) + , part_(first_) + , ptr_(first_) + , last_(first_ + str.size()) + { + } + + void discardPart() + { + part_ = ptr_; + } + + void appendPart(std::string& part) + { + part.append(part_, ptr_); + discardPart(); + } + + std::size_t remaining() const noexcept + { + return last_ - ptr_; + } + + std::size_t consumed() const noexcept + { + return ptr_ - first_; + } + + bool valid() const noexcept + { + return ptr_ != last_; + } + + explicit operator bool() const noexcept + { + return valid(); + } + + char operator*() const noexcept + { + MRDOCS_ASSERT(valid()); + return *ptr_; + } + + bool consume(std::size_t n = 1) + { + MRDOCS_ASSERT(remaining() >= n); + ptr_ += n; + return valid(); + } + + TokenStream& operator++() noexcept + { + consume(); + return *this; + } + + bool tryConsume(std::string_view str) + { + std::string_view data(ptr_, last_); + if(! data.starts_with(str)) + return false; + consume(str.size()); + return true; + } + + template + std::string_view consumeWhile(Predicate&& pred) + { + const auto first = ptr_; + while(valid() && pred(**this)) + consume(); + return std::string_view(first, ptr_); + } + + template + std::string_view consumeUntil(Predicate&& pred) + { + return consumeWhile(std::not_fn( + std::forward(pred))); + } +}; + +// whether a character is a digit, per the C++ grammar +template +bool isDigit(char c) +{ + return (AllowWildcards && c == '*') || + (c >= '0' && c <= '9'); +} + +// whether a character is a non-digit, per the C++ grammar +template +bool isNonDigit(char c) +{ + if((AllowWildcards && c == '*') || c == '_') + return true; + c |= 0x20; + return c >= 'a' && c <= 'z'; +} + +template +class IdExpressionParser +{ + TokenStream s_; + ParseResult& result_; + + enum class IdentifierKind + { + Normal, + Typename, + Template, + Operator, + Decltype + }; + + static bool isIdentifierStart(char c) + { + return c == '~' || isNonDigit(c); + } + + static bool isIdentifierContinue(char c) + { + return c != '~' && (isNonDigit(c) || + isDigit(c)); + } + + template + void + fail( + std::string_view err_msg, + Args&&... err_args) + { + formatError(err_msg, std::forward(err_args)...).Throw(); + } + + void commit() + { + s_.appendPart(result_.name); + } + + void discard() + { + s_.discardPart(); + } + + bool skipWhitespace() + { + s_.consumeWhile([](char c) + { + // we consider carriage returns to be whitespace + return c == ' ' || c == '\n' || c == '\t' || + c == '\v' || c == '\f' || c == '\r'; + }); + discard(); + return ! s_; + } + + template + void + skipWhitespace( + std::string_view err_msg, + Args&&... err_args) + { + if(skipWhitespace()) + fail(err_msg, std::forward(err_args)...); + } + + void + skipBalanced( + char start_tok, + char end_tok) + { + MRDOCS_ASSERT(*s_ == start_tok); + // skip opening token + ++s_; + while(true) + { + s_.consumeWhile([&](char c) + { + return c != start_tok && c != end_tok; + }); + if(! s_) + fail("expected '{}' to match '{}'", end_tok, start_tok); + // found end token, done matching at this level + if(*s_ == end_tok) + break; + skipBalanced(start_tok, end_tok); + } + // skip closing token + ++s_; + } + + void parseColonColon() + { + MRDOCS_ASSERT(*s_ == ':'); + if(! ++s_ || *s_ != ':') + fail("expected ':' after ':'"); + ++s_; + skipWhitespace("expected unqualified-id after '::'"); + } + + IdentifierKind parseIdentifier() + { + MRDOCS_ASSERT(isIdentifierStart(*s_)); + if(! isIdentifierStart(*s_)) + fail("expected identifier-start"); + auto id = s_.consumeWhile(isIdentifierContinue); + if(id == "operator") + return IdentifierKind::Operator; + if(id == "template") + return IdentifierKind::Template; + if(id == "typename") + return IdentifierKind::Typename; + if(id == "decltype") + return IdentifierKind::Decltype; + return IdentifierKind::Normal; + } + + void parseOperator() + { + switch(char first = *s_) + { + case ',': + case '~': + { + ++s_; + break; + } + case '(': + case '[': + { + ++s_; + commit(); + char close = first == '(' ? ')' : ']'; + if(skipWhitespace() || *s_ != close) + fail("expected '{}' after 'operator {}'", close, first); + ++s_; + break; + } + case '*': + case '%': + case '/': + case '^': + case '=': + case '!': + { + if(! ++s_ || *s_ != '=') + break; + ++s_; + break; + } + case '+': + case '|': + case '&': + { + if(! ++s_ || (*s_ != first && *s_ != '=')) + break; + ++s_; + break; + } + case '>': + { + if(! ++s_) + break; + + if(*s_ == '>') + ++s_; + + if(s_ && *s_ == '=') + ++s_; + break; + } + case '<': + { + if(! ++s_) + break; + + bool is_shift = *s_ == '<'; + if(is_shift) + ++s_; + + if(s_ && *s_ == '=') + { + if(++s_ && ! is_shift && *s_ == '>') + ++s_; + } + break; + } + case '-': + { + if(! ++s_) + break; + if(*s_ == '-' || *s_ == '=') + ++s_; + else if(*s_ == '>' && ++s_ && *s_ == '*') + ++s_; + break; + } + case 'c': + { + if(! s_.tryConsume("co_await")) + fail("invalid operator name"); + break; + } + case 'n': + case 'd': + { + std::string_view name = + first == 'n' ? "new" : "delete"; + if(! s_.tryConsume(name)) + fail("invalid operator name"); + commit(); + // consume [], if present + if(! skipWhitespace() && *s_ == '[') + { + commit(); + if(skipWhitespace() || *s_ != ']') + fail("expected ']' after 'operator {}['", name); + ++s_; + } + break; + } + default: + fail("invalid operator name"); + break; + } + commit(); + } + + bool parseName() + { + switch(parseIdentifier()) + { + // simple identifier + case IdentifierKind::Normal: + // store the identifier + commit(); + + // parse the template argument list, if any + if(! skipWhitespace() && *s_ == '<') + { + // parse the template argument list + skipBalanced('<', '>'); + commit(); + } + return false; + // operator name or conversion-function-id + case IdentifierKind::Operator: + // store 'operator' + commit(); + skipWhitespace("expected operator name"); + // parse the tokens after 'operator'. + // KRYSTIAN TODO: support conversion functions + parseOperator(); + + // parse the template argument list, if any + if(! skipWhitespace() && *s_ == '<') + { + // parse the template argument list + skipBalanced('<', '>'); + commit(); + } + // operator names are always terminal + return true; + // 'template' followed by simple-template-id + case IdentifierKind::Template: + // KRYSTIAN FIXME: do we want to store this? + // discard 'template' + discard(); + + skipWhitespace("expected template-id after 'template'"); + + // KRYSTIAN FIXME: we should restrict the names permitted + // to come after 'template' here + return parseName(); + // typename-specifier + case IdentifierKind::Typename: + // KRYSTIAN FIXME: do we want to store this? + // discard 'typename' + discard(); + + skipWhitespace("expected nested-name-specifier after 'typename'"); + + // KRYSTIAN FIXME: we should restrict the names permitted + // to come after 'typename' here + return parseName(); + // decltype-specifier + case IdentifierKind::Decltype: + // store 'decltype' + commit(); + + if(skipWhitespace() || *s_ != '(') + fail("expected '(' after 'decltype'"); + + // parse the operand of the decltype-specifier + skipBalanced('(', ')'); + commit(); + return false; + default: + MRDOCS_UNREACHABLE(); + } + } + +public: + IdExpressionParser( + std::string_view str, + ParseResult& result) + : s_(str) + , result_(result) + { + } + + void parse() + { + skipWhitespace("expected id-expression"); + + // qualified-id starting with '::' + if(*s_ == ':') + { + result_.qualified = true; + parseColonColon(); + } + + // now parse the optional nested-name-specifier, + // followed by an unqualified-id + while(! parseName()) + { + // if we are out of characters, or if we have a possibly + // invalid character following the name (which could be + // the parameter types), stop parsing + if(skipWhitespace() || *s_ != ':') + break; + + result_.qualified = true; + result_.qualifier.emplace_back( + std::move(result_.name)); + result_.name.clear(); + + parseColonColon(); + } + + // parse parameter types + if(s_ && *s_ == '(') + { + discard(); + // KRYSTIAN FIXME: right now we drop the parameters + skipBalanced('(', ')'); + } + } +}; + +} // (anon) + +Expected +parseIdExpression( + std::string_view str, + bool allow_wildcards) +{ + try + { + ParseResult result; + if(allow_wildcards) + IdExpressionParser(str, result).parse(); + else + IdExpressionParser(str, result).parse(); + return std::move(result); + } + catch(const Exception& ex) + { + return Unexpected(ex.error()); + } +} + +} // mrdocs +} // clang diff --git a/src/lib/Support/NameParser.hpp b/src/lib/Support/NameParser.hpp new file mode 100644 index 000000000..64a6a31e4 --- /dev/null +++ b/src/lib/Support/NameParser.hpp @@ -0,0 +1,38 @@ +// +// 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) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_SUPPORT_NAMEPARSER_HPP +#define MRDOCS_LIB_SUPPORT_NAMEPARSER_HPP + +#include +#include +#include +#include +#include + +namespace clang { +namespace mrdocs { + +struct ParseResult +{ + bool qualified = false; + std::vector qualifier; + std::string name; +}; + +Expected +parseIdExpression( + std::string_view str, + bool allow_wildcards = false); + +} // mrdocs +} // clang + +#endif diff --git a/test-files/old-tests/ref.cpp b/test-files/old-tests/ref.cpp new file mode 100644 index 000000000..d4f1d0347 --- /dev/null +++ b/test-files/old-tests/ref.cpp @@ -0,0 +1,176 @@ +void f0(); + +namespace A +{ + + /** + See @ref f0 + + See @ref ::f0 + */ + void f1(); + + /** + See @ref f1 + + See @ref A::f1 + + See @ref ::A::f1 + */ + template + struct B + { + void f2(); + }; + + struct C + { + void f3(); + void f4(); + }; + + struct D : C + { + void f4(); + + /** + See @ref f3 + + See @ref f4 + + See @ref C::f4 + */ + struct E { }; + }; +} + +/** + See @ref A::f1 + + See @ref ::A::f1 +*/ +void f5(); + +struct F +{ + void operator~(); + void operator,(F&); + void operator()(F&); + void operator[](F&); + void operator+(F&); + void operator++(); + void operator+=(F&); + void operator&(F&); + void operator&&(F&); + void operator&=(F&); + void operator|(F&); + void operator||(F&); + void operator|=(F&); + void operator-(F&); + void operator--(); + void operator-=(F&); + void operator->(); + void operator->*(F&); + void operator<(F&); + void operator<<(F&); + void operator<<=(F&); + void operator<=(F&); + void operator<=>(F&); + void operator>(F&); + void operator>>(F&); + void operator>>=(F&); + void operator>=(F&); + void operator*(F&); + void operator*=(F&); + void operator%(F&); + void operator%=(F&); + void operator/(F&); + void operator/=(F&); + void operator^(F&); + void operator^=(F&); + void operator=(F&); + void operator==(F&); + void operator!(); + void operator!=(F&); +}; + +/** + See @ref F::operator~ + + See @ref F::operator, + + See @ref F::operator() + + See @ref F::operator[] + + See @ref F::operator+ + + See @ref F::operator++ + + See @ref F::operator+= + + See @ref F::operator& + + See @ref F::operator&& + + See @ref F::operator&= + + See @ref F::operator| + + See @ref F::operator|| + + See @ref F::operator|= + + See @ref F::operator- + + See @ref F::operator-- + + See @ref F::operator-= + + See @ref F::operator-> + + See @ref F::operator->* + + See @ref F::operator< + + See @ref F::operator<< + + See @ref F::operator<<= + + See @ref F::operator<= + + See @ref F::operator<=> + + See @ref F::operator> + + See @ref F::operator>> + + See @ref F::operator>>= + + See @ref F::operator>= + + See @ref F::operator* + + See @ref F::operator*= + + See @ref F::operator% + + See @ref F::operator%= + + See @ref F::operator/ + + See @ref F::operator/= + + See @ref F::operator^ + + See @ref F::operator^= + + See @ref F::operator= + + See @ref F::operator== + + See @ref F::operator! + + See @ref F::operator!= +*/ +void f6(); diff --git a/test-files/old-tests/ref.xml b/test-files/old-tests/ref.xml new file mode 100644 index 000000000..763020e36 --- /dev/null +++ b/test-files/old-tests/ref.xml @@ -0,0 +1,585 @@ + + + + + + + + + + + + See + f0 + + + See + ::f0 + + + + + + + + + + + + + + + + + + + + + + + + + + See + f3 + + + See + f4 + + + See + C::f4 + + + + + + + + + + See + A::f1 + + + See + ::A::f1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + See + F::operator~ + + + See + F::operator, + + + See + F::operator() + + + See + F::operator[] + + + See + F::operator+ + + + See + F::operator++ + + + See + F::operator+= + + + See + F::operator& + + + See + F::operator&& + + + See + F::operator&= + + + See + F::operator| + + + See + F::operator|| + + + See + F::operator|= + + + See + F::operator- + + + See + F::operator-- + + + See + F::operator-= + + + See + F::operator-> + + + See + F::operator->* + + + See + F::operator< + + + See + F::operator<< + + + See + F::operator<<= + + + See + F::operator<= + + + See + F::operator<=> + + + See + F::operator> + + + See + F::operator>> + + + See + F::operator>>= + + + See + F::operator>= + + + See + F::operator* + + + See + F::operator*= + + + See + F::operator% + + + See + F::operator%= + + + See + F::operator/ + + + See + F::operator/= + + + See + F::operator^ + + + See + F::operator^= + + + See + F::operator= + + + See + F::operator== + + + See + F::operator! + + + See + F::operator!= + + + + +