From 7e5fc6e235d3bc7cb785ae5c49b8de5f2846e299 Mon Sep 17 00:00:00 2001 From: alandefreitas <alandefreitas@gmail.com> Date: Thu, 27 Feb 2025 19:31:27 -0300 Subject: [PATCH 1/4] Parse support header #improvement --- include/mrdocs/Metadata/Template.hpp | 4 +- include/mrdocs/Support/Error.hpp | 4 +- include/mrdocs/Support/Parse.hpp | 150 +++++++++++++++++++++++++++ src/lib/AST/ParseRef.cpp | 22 +++- src/lib/AST/ParseRef.hpp | 8 +- src/test/lib/AST/ParseRef.cpp | 4 +- 6 files changed, 180 insertions(+), 12 deletions(-) create mode 100644 include/mrdocs/Support/Parse.hpp diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index b7b9d8156..b9572c39c 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -34,7 +34,9 @@ enum class TArgKind : int Template }; -MRDOCS_DECL std::string_view toString(TArgKind kind) noexcept; +MRDOCS_DECL +std::string_view +toString(TArgKind kind) noexcept; inline void diff --git a/include/mrdocs/Support/Error.hpp b/include/mrdocs/Support/Error.hpp index aac800bb5..dbef752fd 100644 --- a/include/mrdocs/Support/Error.hpp +++ b/include/mrdocs/Support/Error.hpp @@ -53,7 +53,7 @@ class MRDOCS_DECL A default constructed error is equivalent to success. */ - Error() noexcept = delete; + Error() noexcept = default; /** Constructor. */ @@ -122,7 +122,7 @@ class MRDOCS_DECL constexpr bool failed() const noexcept { - return ! message_.empty(); + return !message_.empty(); } /** Return true if this holds an error. diff --git a/include/mrdocs/Support/Parse.hpp b/include/mrdocs/Support/Parse.hpp new file mode 100644 index 000000000..c570bc491 --- /dev/null +++ b/include/mrdocs/Support/Parse.hpp @@ -0,0 +1,150 @@ +// +// 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_API_SUPPORT_PARSE_HPP +#define MRDOCS_API_SUPPORT_PARSE_HPP + +#include <mrdocs/Support/Error.hpp> +#include <mrdocs/Support/Expected.hpp> + +namespace clang::mrdocs { + +/** The result of a parse operation. + + This class holds the result of a parse operation. + The structure is similar to `std::from_chars_result`, + where we have a `ptr` member that points to the + first character not parsed, and a `ec` member that + holds the error code. + + If parsing was successful, then `ec` stores + a default constructed `Error` object, which + indicates success. The `operator bool` can + also be used to check for success. + + The typical format of a parsing function is: + + @code + ParseResult + parseType( + char const* first, + char const* last, + Type& value); + @endcode + + where more parameters can be defined as needed for + parsing options. + */ +struct ParseResult { + const char* ptr; + Error ec; + + friend + bool + operator==( + const ParseResult&, + const ParseResult& ) = default; + + constexpr + explicit + operator bool() const noexcept + { + return !ec.failed(); + } +}; + +/** Concept to determine if there's a parse function for a type. + + This concept checks if a type `T` has a parse function + with the signature: + + @code + ParseResult + parse( + char const* first, + char const* last, + T& value); + @endcode + */ +template <class T> +concept HasParse = requires( + char const* first, + char const* last, + T& value) +{ + { parse(first, last, value) } -> std::same_as<ParseResult>; +}; + +/** Parse a string view + + This function parses a string view `sv` into a value + of type `T`. The function calls the `parse` function + for the type `T` with the `sv.data()` and `sv.data() + sv.size()` + as the first and last pointers, respectively. + + If the parse function returns an error, then the function + returns the error. + + If the parse function returns success, but there are + characters left in the string view, then the function + returns an error with the message "trailing characters". + + Otherwise, it returns the value. + + @param sv The string view to parse + @param value The value to store the result + */ +template <HasParse T> +ParseResult +parse(std::string_view sv, T& value) +{ + ParseResult result = parse(sv.data(), sv.data() + sv.size(), value); + if (result) + { + if (result.ptr != sv.data() + sv.size()) + { + result.ec = Error("trailing characters"); + } + } + return result; +} + +/** Parse a string view + + Parse a string view `sv` as an object of type `T`. + If parsing fails, the function returns an error. + + This overload does not return the `ParseResult` object + containing the pointer to the first character not parsed. + Instead, the position of the error is calculated and + the error message is formatted with the error position. + + @copydetails parse(std::string_view, T&) + + */ +template <HasParse T> +Expected<T> +parse(std::string_view sv) +{ + T v; + ParseResult const result = parse(sv, v); + if (result) + { + return v; + } + std::size_t const pos = result.ptr - sv.data(); + return Unexpected(formatError( + "'{}' at position {}: {}", + sv, pos, result.ec.reason())); +} + +} // clang::mrdocs + +#endif \ No newline at end of file diff --git a/src/lib/AST/ParseRef.cpp b/src/lib/AST/ParseRef.cpp index b5cff716e..3e4e71917 100644 --- a/src/lib/AST/ParseRef.cpp +++ b/src/lib/AST/ParseRef.cpp @@ -2139,12 +2139,24 @@ class RefParser } // (anon) // Function to parse a C++ symbol name -Expected<ParsedRef> -parseRef(std::string_view sv) +ParseResult +parse( + char const* first, + char const* last, + ParsedRef& value) { - RefParser parser(sv); - parser.parse(); - return parser.result(); + RefParser parser(first, last, value); + ParseResult res; + if (parser.parse()) + { + res.ptr = parser.position(); + } + else + { + res.ec = parser.error(); + res.ptr = parser.errorPos(); + } + return res; } } // clang::mrdocs diff --git a/src/lib/AST/ParseRef.hpp b/src/lib/AST/ParseRef.hpp index 8ea15152d..9765991c2 100644 --- a/src/lib/AST/ParseRef.hpp +++ b/src/lib/AST/ParseRef.hpp @@ -13,6 +13,7 @@ #include <mrdocs/Metadata/Specifiers.hpp> #include <mrdocs/Metadata/Type.hpp> +#include <mrdocs/Support/Parse.hpp> #include <mrdocs/ADT/Polymorphic.hpp> #include <llvm/ADT/SmallVector.h> #include <string_view> @@ -70,8 +71,11 @@ struct ParsedRef { NoexceptInfo ExceptionSpec; }; -Expected<ParsedRef> -parseRef(std::string_view sv); +ParseResult +parse( + char const* first, + char const* last, + ParsedRef& value); } // clang::mrdocs diff --git a/src/test/lib/AST/ParseRef.cpp b/src/test/lib/AST/ParseRef.cpp index ff3920894..0811a53fd 100644 --- a/src/test/lib/AST/ParseRef.cpp +++ b/src/test/lib/AST/ParseRef.cpp @@ -15,8 +15,8 @@ namespace clang::mrdocs { struct ParseRef_test { -#define ok(str) BOOST_TEST(parseRef(str).has_value()) -#define fail(str) BOOST_TEST(!parseRef(str).has_value()) +#define ok(str) BOOST_TEST(parse<ParsedRef>(str).has_value()) +#define fail(str) BOOST_TEST(!parse<ParsedRef>(str).has_value()) void testComponents() From 7628bd08d55c8ea0fed5d7d405b1766686a4d8c9 Mon Sep 17 00:00:00 2001 From: alandefreitas <alandefreitas@gmail.com> Date: Thu, 27 Feb 2025 19:33:15 -0300 Subject: [PATCH 2/4] ref parser supports template arguments #feat --- include/mrdocs/Support/String.hpp | 9 + src/lib/AST/ParseRef.cpp | 310 ++++++++++++++++++------------ src/lib/AST/ParseRef.hpp | 3 +- src/test/lib/AST/ParseRef.cpp | 19 +- 4 files changed, 213 insertions(+), 128 deletions(-) diff --git a/include/mrdocs/Support/String.hpp b/include/mrdocs/Support/String.hpp index 4aa9233e5..dc17ec96c 100644 --- a/include/mrdocs/Support/String.hpp +++ b/include/mrdocs/Support/String.hpp @@ -77,6 +77,15 @@ MRDOCS_DECL void replace(std::string& s, std::string_view from, std::string_view to); +/** Determine if a string is only whitespace. + */ +constexpr +bool +isWhitespace(std::string_view s) noexcept +{ + return s.find_first_not_of(" \t\n\v\f\r") == std::string::npos; +} + } // mrdocs } // clang diff --git a/src/lib/AST/ParseRef.cpp b/src/lib/AST/ParseRef.cpp index 3e4e71917..6a279bb78 100644 --- a/src/lib/AST/ParseRef.cpp +++ b/src/lib/AST/ParseRef.cpp @@ -64,18 +64,21 @@ class RefParser char const* first_; char const* ptr_; char const* last_; - ParsedRef result_; - std::string error_; + ParsedRef& result_; + std::string error_msg_; char const* error_pos_{nullptr}; public: explicit - RefParser(std::string_view const str) - : first_(str.data()) - , ptr_(first_) - , last_(first_ + str.size()) - { - } + RefParser( + char const* first, + char const* last, + ParsedRef& result) noexcept + : first_(first), + ptr_(first), + last_(last), + result_(result) + {} bool parse() @@ -85,8 +88,7 @@ class RefParser { result_.IsFullyQualified = true; } - MRDOCS_CHECK_OR(parseComponents(), false); - skipWhitespace(); + MRDOCS_CHECK_OR(parseComponents(result_.Components), false); result_.HasFunctionParameters = peek('(', ' '); if (result_.HasFunctionParameters) { @@ -100,21 +102,27 @@ class RefParser result_.Kind = functionParameters.Kind; result_.IsExplicitObjectMemberFunction = functionParameters.IsExplicitObjectMemberFunction; } - error_.clear(); + error_msg_.clear(); error_pos_ = nullptr; return true; } - Expected<ParsedRef> - result() const + Error + error() const { - if (error_pos_ == nullptr) - { - return result_; - } - std::string_view str(first_, last_ - first_); - std::size_t pos = error_pos_ - first_; - return Unexpected(formatError("'{}' at position {}: {}", str, pos, error_)); + return Error(error_msg_); + } + + char const* + errorPos() const + { + return error_pos_; + } + + char const* + position() const + { + return ptr_; } private: @@ -123,9 +131,9 @@ class RefParser { // Only set the error if it's not already set // with a more specific error message - if (!error_pos_ || error_.empty()) + if (!error_pos_ || error_msg_.empty()) { - error_ = std::string(str); + error_msg_ = std::string(str); error_pos_ = pos; } } @@ -242,6 +250,21 @@ class RefParser return first != last_ && *first == c; } + bool + peek(std::string_view str, char skip) + { + char const* first = ptr_; + while (first != last_ && *first == skip) + { + ++first; + } + if (std::cmp_greater(str.size(), last_ - first)) + { + return false; + } + return std::equal(str.begin(), str.end(), first); + } + bool peekBack(char c, char skip) { @@ -276,8 +299,27 @@ class RefParser } bool - rewindUntil(char c) + advance(char c) + { + char const* start = ptr_; + while (ptr_ != last_ && *ptr_ == c) + { + ++ptr_; + } + return ptr_ != start; + } + + bool + rewindUntil(char const c) { + if (first_ == last_) + { + return false; + } + if (ptr_ == last_) + { + --ptr_; + } while (ptr_ != first_ && *ptr_ != c) { --ptr_; @@ -286,25 +328,26 @@ class RefParser } bool - parseComponents() + parseComponents(llvm::SmallVector<ParsedRefComponent, 8>& components) { char const* start = ptr_; while (true) { char const* compStart = ptr_; - if (!parseComponent()) + if (!parseComponent(result_.Components.emplace_back())) { return false; } - skipWhitespace(); - if (!parseLiteral("::")) + if (!peek("::", ' ')) { - return !result_.Components.empty(); + return !components.empty(); } + skipWhitespace(); + parseLiteral("::"); // If we have a "::" separator, so this is not // the last component. Check the rules for // nested-name-specifier - ParsedRefComponent const& comp = result_.Components.back(); + ParsedRefComponent const& comp = components.back(); if (comp.isOperator()) { ptr_ = compStart; @@ -323,7 +366,7 @@ class RefParser } bool - parseComponent() + parseComponent(ParsedRefComponent& c) { if (!hasMore()) { @@ -332,28 +375,26 @@ class RefParser } char const *start = ptr_; skipWhitespace(); - result_.Components.emplace_back(); - if (!parseUnqualifiedIdentifierExpression()) + if (!parseUnqualifiedId(c)) { setError("expected component name"); ptr_ = start; return false; } - skipWhitespace(); - if (peek('<')) { - if (!parseTemplateArguments()) + if (peek('<', ' ')) { + skipWhitespace(); + if (!parseTemplateArguments(c.TemplateArguments)) { setError("expected template arguments"); ptr_ = start; return false; } } - return true; } bool - parseUnqualifiedIdentifierExpression() + parseUnqualifiedId(ParsedRefComponent& c) { // https://en.cppreference.com/w/cpp/language/identifiers#Unqualified_identifiers // Besides suitably declared identifiers, the following unqualified identifier @@ -374,31 +415,30 @@ class RefParser } // Try to parse as an operator - if (parseOperator()) + if (parseOperator(c)) { return true; } // Parse conversion operator - if (parseConversionOperator()) + if (parseConversionOperator(c)) { return true; } // Parse as a regular identifier - if (!parseDestructorOrIdentifier()) + if (!parseDestructorOrIdentifier(c.Name)) { setError("expected component name"); ptr_ = start; return false; } - currentComponent().Name = std::string_view(start, ptr_ - start); - currentComponent().Operator = OperatorKind::None; + c.Operator = OperatorKind::None; return true; } bool - parseConversionOperator() + parseConversionOperator(ParsedRefComponent& c) { char const* start = ptr_; if (!parseKeyword("operator")) @@ -413,12 +453,12 @@ class RefParser ptr_ = start; return false; } - currentComponent().ConversionType = std::move(conversionType); + c.ConversionType = std::move(conversionType); return true; } bool - parseDestructorOrIdentifier() + parseDestructorOrIdentifier(std::string_view& s) { // A regular identifier or a function name char const* start = ptr_; @@ -438,6 +478,7 @@ class RefParser ptr_ = start; return false; } + s = std::string_view(start, ptr_ - start); return true; } @@ -477,14 +518,8 @@ class RefParser return true; } - ParsedRefComponent& - currentComponent() - { - return result_.Components.back(); - } - bool - parseOperator() + parseOperator(ParsedRefComponent& c) { const char* start = ptr_; if (!parseLiteral("operator")) @@ -501,9 +536,9 @@ class RefParser { if (parseLiteral(op)) { - currentComponent().Operator = getOperatorKindFromSuffix(op); - MRDOCS_ASSERT(currentComponent().Operator != OperatorKind::None); - currentComponent().Name = getOperatorName(currentComponent().Operator, true); + c.Operator = getOperatorKindFromSuffix(op); + MRDOCS_ASSERT(c.Operator != OperatorKind::None); + c.Name = getOperatorName(c.Operator, true); return true; } } @@ -526,21 +561,21 @@ class RefParser return false; } std::string_view op(op_start, ptr_ - op_start); - currentComponent().Operator = getOperatorKindFromSuffix(op); - if (currentComponent().Operator == OperatorKind::None) + c.Operator = getOperatorKindFromSuffix(op); + if (c.Operator == OperatorKind::None) { // This operator doesn't exist ptr_ = start; return false; } - currentComponent().Name = getOperatorName(currentComponent().Operator, true); + c.Name = getOperatorName(c.Operator, true); return true; } bool - parseTemplateArguments() + parseTemplateArguments(std::vector<Polymorphic<TArg>>& TemplateArguments) { - // https://en.cppreference.com/w/cpp/language/template_parameters + // https://en.cppreference.com/w/cpp/language/template_parameters#Template_arguments char const* start = ptr_; if (!parseLiteral('<')) { @@ -548,12 +583,14 @@ class RefParser return false; } skipWhitespace(); - while (parseTemplateArgument()) + TemplateArguments.emplace_back(); + while (parseTemplateArgument(TemplateArguments.back())) { skipWhitespace(); if (parseLiteral(',')) { skipWhitespace(); + TemplateArguments.emplace_back(); } else { @@ -570,69 +607,49 @@ class RefParser return true; } - bool - parseTemplateArgument() - { - return parseTemplateArgument(currentComponent().TemplateArguments.emplace_back()); - } - bool parseTemplateArgument(Polymorphic<TArg>& dest) { + // https://en.cppreference.com/w/cpp/language/template_parameters#Template_arguments + // If an argument can be interpreted as both a type-id and an + // expression, it is always interpreted as a type-id, even if the + // corresponding template parameter is non-type: if (!hasMore()) { return false; } skipWhitespace(); char const* start = ptr_; - if (!parseTemplateArgumentName()) + Polymorphic<TypeInfo> type; + if (parseTypeId(type)) { - ptr_ = start; - setError("expected template argument name"); - return false; + TypeTArg arg; + arg.Type = std::move(type); + dest = std::move(arg); + return true; } - skipWhitespace(); - return true; - } - - Polymorphic<TArg>& - currentTemplateArgument() - { - return currentComponent().TemplateArguments.back(); - } - - bool - parseTemplateArgumentName() { - auto& dest = currentTemplateArgument(); - return parseTemplateArgumentName(dest); - } - - bool - parseTemplateArgumentName(Polymorphic<TArg>& dest) - { - char const* start = ptr_; - if (!hasMore()) + // If the argument is not a type-id, it is an expression + // The expression is internally balanced in regards to '<' + // and '>' and ends with a comma + char const* exprStart = ptr_; + while (parseBalanced("<", ">", {",", ">"})) { - setError("expected component name"); - return false; + if (!peekAny({',', '>'}, ' ')) + { + continue; + } + break; } - - // Parse as a regular identifier - if (!parseIdentifier(true)) + if (ptr_ == exprStart) { - setError("expected identifier name"); + setError("expected template argument"); ptr_ = start; return false; } - auto const nameStr = std::string_view(start, ptr_ - start); - TypeTArg arg; - NamedTypeInfo type; - NameInfo nameInfo; - nameInfo.Name = std::string(nameStr); - type.Name = nameInfo; - arg.Type = type; - dest = arg; + NonTypeTArg arg; + arg.Value.Written = trim(std::string_view(exprStart, ptr_ - exprStart)); + dest = std::move(arg); return true; } @@ -774,22 +791,10 @@ class RefParser // decl-specifier-seq dest.Params.emplace_back(); auto& curParam = dest.Params.back(); - if (!parseDeclarationSpecifiers(curParam)) - { - ptr_ = start; - setError("expected parameter qualified type"); - return false; - } - - // If a parameter is not used in the function body, it does - // not need to be named (it's sufficient to use an - // abstract declarator). - // MrDocs refs only use abstract declarators. Any parameter - // name is ignored. - if (!parseAbstractDeclarator(curParam)) + if (!parseTypeId(curParam)) { - setError("expected abstract declarator"); ptr_ = start; + setError("expected type-id"); return false; } @@ -817,6 +822,35 @@ class RefParser return true; } + bool + parseTypeId(Polymorphic<TypeInfo>& dest) + { + char const* start = ptr_; + + // https://en.cppreference.com/w/cpp/language/function#Parameter_list + // decl-specifier-seq + if (!parseDeclarationSpecifiers(dest)) + { + ptr_ = start; + setError("expected parameter qualified type"); + return false; + } + + // If a parameter is not used in the function body, it does + // not need to be named (it's sufficient to use an + // abstract declarator). + // MrDocs refs only use abstract declarators. Any parameter + // name is ignored. + if (!parseAbstractDeclarator(dest)) + { + setError("expected abstract declarator"); + ptr_ = start; + return false; + } + + return true; + } + bool parseDeclarationSpecifiers(Polymorphic<TypeInfo>& dest) { @@ -859,7 +893,7 @@ class RefParser return false; } // Clear the error and let the type modifiers set `dest` - error_.clear(); + error_msg_.clear(); error_pos_ = nullptr; break; } @@ -1267,18 +1301,35 @@ class RefParser bool parseBalanced( std::string_view const openTag, - std::string_view const closeTag) + std::string_view const closeTag, + std::initializer_list<std::string_view> const until = {}) { char const* start = ptr_; std::size_t depth = 0; while (hasMore()) { + if (depth == 0) + { + for (std::string_view const& untilTag : until) + { + if (peek(untilTag)) + { + return true; + } + } + } if (parseLiteral(openTag)) { ++depth; } else if (parseLiteral(closeTag)) { + if (depth == 0) + { + setError("unbalanced expression"); + ptr_ = start; + return false; + } --depth; if (depth == 0) { @@ -1360,11 +1411,24 @@ class RefParser skipWhitespace(); if (peek('<')) { - if (!parseTemplateArguments()) + if (!dest->isNamed()) + { + setError("expected named type for template arguments"); + ptr_ = start; + return false; + } + // Replace the nameinfo with a nameinfo with args + auto& namedParam = dynamic_cast<NamedTypeInfo&>(*dest); + SpecializationNameInfo SNI; + SNI.Name = std::move(namedParam.Name->Name); + SNI.Prefix = std::move(namedParam.Name->Prefix); + SNI.id = namedParam.Name->id; + if (!parseTemplateArguments(SNI.TemplateArgs)) { ptr_ = start; return false; } + namedParam.Name = std::move(SNI); } else { diff --git a/src/lib/AST/ParseRef.hpp b/src/lib/AST/ParseRef.hpp index 9765991c2..da27d58b1 100644 --- a/src/lib/AST/ParseRef.hpp +++ b/src/lib/AST/ParseRef.hpp @@ -25,7 +25,8 @@ struct ParsedRefComponent { std::string_view Name; // If not empty, this is a specialization - llvm::SmallVector<Polymorphic<TArg>, 8> TemplateArguments; + bool HasTemplateArguments = false; + std::vector<Polymorphic<TArg>> TemplateArguments; // If not None, this is an operator // Only the last component can be an operator diff --git a/src/test/lib/AST/ParseRef.cpp b/src/test/lib/AST/ParseRef.cpp index 0811a53fd..a7837e711 100644 --- a/src/test/lib/AST/ParseRef.cpp +++ b/src/test/lib/AST/ParseRef.cpp @@ -24,7 +24,6 @@ testComponents() fail(""); ok("a"); ok(" a"); - ok(" a "); ok("::a"); ok("a::b"); ok("a::b::c"); @@ -32,8 +31,8 @@ testComponents() ok("a:: ~ b"); ok("a::operator+"); ok("a::operator()"); - ok("a:: operator () "); - fail("a:: operator ( ) "); + ok("a:: operator ()"); + fail("a:: operator ( )"); ok("a::operator bool"); fail("a::operator bool::c"); fail("a::operator+::c"); @@ -43,7 +42,7 @@ void testFunctionParameters() { ok("f()"); - ok("f ( ) "); + ok("f ( )"); ok("f(void)"); fail("f(void, void)"); fail("f(int, void)"); @@ -294,6 +293,17 @@ testMainFunctionQualifiers() ok("f(int) && noexcept"); } +void +testTemplateArguments() +{ + // type template parameter + ok("A::B<int>"); + ok("A::B<int, 2>"); + ok("A::B<int, true>"); + ok("A::B<C, true>"); + ok("A::B<Args..., true>"); +} + void run() { @@ -302,6 +312,7 @@ run() testParameterDeclarationSpecifiers(); testParameterDeclarators(); testMainFunctionQualifiers(); + testTemplateArguments(); } }; From d6d48393d22b9deff3d532aeae6b32523e035a86 Mon Sep 17 00:00:00 2001 From: alandefreitas <alandefreitas@gmail.com> Date: Thu, 27 Feb 2025 19:34:18 -0300 Subject: [PATCH 3/4] javadoc visitor uses ref parser #improvement --- src/lib/AST/ParseJavadoc.cpp | 332 ++++++++++++----------------------- 1 file changed, 113 insertions(+), 219 deletions(-) diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp index 081a9e2b3..aada63079 100644 --- a/src/lib/AST/ParseJavadoc.cpp +++ b/src/lib/AST/ParseJavadoc.cpp @@ -23,6 +23,8 @@ #include <clang/AST/RawCommentList.h> #include <clang/Lex/Lexer.h> #include <clang/Basic/SourceLocation.h> +#include "lib/AST/ParseRef.hpp" + #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 5054) // C5054: operator '+': deprecated between enumerations of different types @@ -1072,6 +1074,21 @@ std::string JavadocVisitor:: fixReference(std::string& ref) { + auto peekNextIt = [&]() -> std::optional<std::string_view> + { + ++it_; + if (it_ == end_ || + (*it_)->getCommentKind() != CommentKind::TextComment) + { + --it_; + return std::nullopt; + } + Comment const* c = *it_; + std::string_view text = static_cast<TextComment const*>(c)->getText(); + --it_; + return text; + }; + // If the ref is only "operator", the next text comment // might contain a simple operator name/type, or a // full operator overload. @@ -1080,249 +1097,126 @@ fixReference(std::string& ref) // we find an unbalanced '('. // Simply including the next text comment is enough // for the next step. - std::string_view trimmed = trim(ref); - bool const isNoSuffixOperator = - trimmed == "operator" || - trimmed.ends_with("::operator"); - if (isNoSuffixOperator) - { - ++it_; - if (it_ == end_) - { - return ref; - } - Comment const* c = *it_; - if (c->getCommentKind() == CommentKind::TextComment) - { - ref += static_cast<TextComment const*>(c)->getText(); - } - else + ParsedRef v; + while (true) + { + // Attempt to parse ref + char const* first = ref.data(); + char const* last = first + ref.size(); + auto const pres = parse(first, last, v); + if (!pres) { - return ref; - } - } - static constexpr std::string_view idChars = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789" - "_:"; - bool const isNoFunctionOperator = - isNoSuffixOperator || - [trimmed]{ - if (contains_n(trimmed, '(', 1)) - { - return false; - } - std::size_t pos = trimmed.rfind("::"); - std::string_view last = trimmed; - if (pos != std::string::npos) { - last = trimmed.substr(pos + 2); - } - if (!last.starts_with("operator")) - { - return false; - } - last.remove_prefix(8); - if (last.empty()) + // The ref could not be parsed, add content from next + // text comment to the ref + auto const nextTextOpt = peekNextIt(); + if (!nextTextOpt) { - return true; + return {}; } - return !contains(idChars, last.front()); - }(); - - // Clang parses the copydoc command breaking - // before the complete overload information. For instance, - // `@copydoc operator()(unsigned char) const` will create - // a node with the text `operator()(unsigned` and another - // with `char) const`. We need to merge these nodes. - // If the ref contains an unbalanced '(', then it's - // a function, and we need to merge the next text comments - // until we find a balanced ')'. - bool const isFunction = contains(ref, '('); - if (isFunction) - { - while (std::ranges::count(ref, '(') != std::ranges::count(ref, ')')) - { + ref += *nextTextOpt; ++it_; - if (it_ == end_) - { - break; - } - Comment const* c = *it_; - if (c->getCommentKind() == CommentKind::TextComment) - { - ref += static_cast<TextComment const*>(c)->getText(); - } - else + continue; + } + + // The ref is fully parsed + if (pres.ptr != last) + { + // The ref didn't consume all the text, so we need to + // remove the leftover text from the ref and return it + auto leftover = std::string(pres.ptr, last - pres.ptr); + // If leftover is only whitespace, the ref might need + // the next text comment to complete it. + if (!isWhitespace(leftover)) { - break; + ref.erase(pres.ptr - first); + return leftover; } } - if (rtrim(ref).ends_with(')')) + + // The ref is fully parsed, but we might want to + // include the next text comment if it contains + // a valid continuation to the ref. + bool const mightHaveMoreQualifiers = + v.HasFunctionParameters && + v.ExceptionSpec.Implicit && + v.ExceptionSpec.Operand.empty(); + if (mightHaveMoreQualifiers) { - static constexpr std::array<std::string_view, 5> qualifiers = { - "const", - "volatile", - "noexcept", - "&&", - "&", - }; - auto isQualifiersOnly = [](std::string_view str) + llvm::SmallVector<std::string_view, 4> potentialQualifiers; + if (v.Kind == ReferenceKind::None) { - // Iterate all words between spaces and check if - // they are qualifiers - std::size_t pos = 0; - while (pos < str.size()) + // "&&" or "&" not defined yet + if (!v.IsConst) { - std::size_t const start = str.find_first_not_of(' ', pos); - if (start == std::string::npos) - { - break; - } - std::size_t const end = str.find_first_of(' ', start); - std::string_view word = str.substr(start, end - start); - if (std::ranges::find(qualifiers, word) == qualifiers.end()) - { - return false; - } - pos = end; + potentialQualifiers.push_back("const"); } - return true; - }; - auto isWhitespaceOnly = [](std::string_view str) - { - return str.empty() || str.find_first_not_of(' ') == std::string::npos; - }; - - // peek next comment - std::string functionContinuation; - auto originalIt = it_; - ++it_; - while ( - it_ != end_ && - (isWhitespaceOnly(functionContinuation) || - isQualifiersOnly(functionContinuation))) - { - Comment const* c = *it_; - if (c->getCommentKind() != CommentKind::TextComment) + if (!v.IsVolatile) { - break; + potentialQualifiers.push_back("volatile"); } - functionContinuation += static_cast<TextComment const*>(c)->getText(); - ++it_; + potentialQualifiers.push_back("&"); } - if (isWhitespaceOnly(functionContinuation)) + else if ( + v.Kind == ReferenceKind::LValue && + ref.ends_with('&')) { - it_ = originalIt; + // The second "&" might be in the next Text block + potentialQualifiers.push_back("&"); } - else /* if (!functionContinuation.empty()) */ + potentialQualifiers.push_back("noexcept"); + auto const nextTextOpt = peekNextIt(); + if (!nextTextOpt) { - --it_; - std::string_view suffix = functionContinuation; - std::string_view leftover = functionContinuation; - bool foundAny = false; - std::size_t totalRemoved = 0; - while (!suffix.empty()) - { - bool found = false; - std::size_t const initialWhitespace = std::min( - suffix.find_first_not_of(" "), suffix.size()); - for (auto const& q : qualifiers) - { - if (suffix.substr(initialWhitespace).starts_with(q)) - { - std::size_t const toRemove = initialWhitespace + q.size(); - if ( - contains(idChars, q.back()) && - suffix.size() > toRemove && - contains(idChars, suffix[toRemove])) - { - // This is not a qualifier, but part of - // an identifier - continue; - } - suffix.remove_prefix(toRemove); - totalRemoved += toRemove; - found = true; - foundAny = true; - break; - } - } - if (!found) + auto leftover = std::string(pres.ptr, last - pres.ptr); + ref.erase(pres.ptr - first); + return leftover; + } + std::string_view const nextText = *nextTextOpt; + std::string_view const trimmed = ltrim(nextText); + if (trimmed.empty() || + std::ranges::any_of( + potentialQualifiers, + [&](std::string_view s) { - break; - } - } - if (foundAny) - { - leftover = leftover.substr(0, totalRemoved); - ref += leftover; - return std::string(suffix); - } + return trimmed.starts_with(s); + })) + { + ref += nextText; + ++it_; + continue; } } - } - - // Clang refs can also contain invalid characters - // at the end, especially punctuation. We need to - // truncate the ref at the last valid identifier - // character. - // The last identifier character depends on the type - // of ref. - // - If it's an operator but not a function, then - // we also consider operator chars as valid. - // - If it's a function, then we also consider ')' - // as valid. - // - In all cases, we consider the identifier chars - // as valid. - static constexpr std::string_view operatorChars = - "~!%^&*()-+=|[]{};:,.<>?/"; - static constexpr std::string_view parenChars = - "()"; - std::string leftover; - bool const isRegularIdentifier = !isFunction && !isNoFunctionOperator; - if (isRegularIdentifier) - { - auto const lastIdChar = ref.find_last_of(idChars); - auto const firstLeftoverChar = lastIdChar + 1; - if (firstLeftoverChar < ref.size()) - { - leftover = std::string_view(ref).substr(lastIdChar + 1); - ref = ref.substr(0, lastIdChar + 1); - } - } - else if (isFunction) - { - auto reservedCharsets = {idChars, parenChars}; - auto reservedChars = std::views::join(reservedCharsets); - auto const lastIdOrParen = find_last_of(ref, reservedChars); - auto const firstLeftoverChar = - lastIdOrParen == ref.end() ? - ref.end() : - std::next(lastIdOrParen); - if (firstLeftoverChar != ref.end()) + // The ref might have more components + bool const mightHaveMoreComponents = + !v.HasFunctionParameters; + if (mightHaveMoreComponents) { - leftover = std::string_view(firstLeftoverChar, ref.end()); - ref = ref.substr(0, std::distance(ref.begin(), firstLeftoverChar)); - } - } - else /* if (isNoFunctionOperator) */ - { - auto reservedCharsets = {idChars, operatorChars}; - auto reservedChars = std::views::join(reservedCharsets); - auto const lastIdOrOperator = find_last_of(ref, reservedChars); - auto const firstLeftoverChar = - lastIdOrOperator == ref.end() ? - ref.end() : - std::next(lastIdOrOperator); - if (firstLeftoverChar != ref.end()) - { - leftover = std::string_view(firstLeftoverChar, ref.end()); - ref = ref.substr(0, std::distance(ref.begin(), firstLeftoverChar)); + auto const nextTextOpt = peekNextIt(); + if (!nextTextOpt) + { + auto leftover = std::string(pres.ptr, last - pres.ptr); + ref.erase(pres.ptr - first); + return leftover; + } + std::string_view const nextText = *nextTextOpt; + std::string_view const trimmed = ltrim(nextText); + static constexpr std::string_view idChars + = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "_:"; + if (trimmed.empty() || + contains(idChars, trimmed.front())) + { + ref += nextText; + ++it_; + continue; + } } + + return {}; } - return leftover; } //------------------------------------------------ From 083338126433cd862667b3d97e325e5d149d9c0c Mon Sep 17 00:00:00 2001 From: alandefreitas <alandefreitas@gmail.com> Date: Thu, 27 Feb 2025 19:34:47 -0300 Subject: [PATCH 4/4] corpus lookup matches template arguments #feat --- mrdocs.rnc | 5 +- src/lib/AST/ParseJavadoc.cpp | 10 +- src/lib/Lib/CorpusImpl.cpp | 67 ++- .../javadoc/copydoc/template-arguments.adoc | 367 ++++++++++++++ .../javadoc/copydoc/template-arguments.cpp | 61 +++ .../javadoc/copydoc/template-arguments.html | 460 ++++++++++++++++++ .../javadoc/copydoc/template-arguments.xml | 167 +++++++ .../golden-tests/javadoc/ref/punctuation.adoc | 75 +++ .../golden-tests/javadoc/ref/punctuation.cpp | 9 + .../golden-tests/javadoc/ref/punctuation.html | 91 ++++ .../golden-tests/javadoc/ref/punctuation.xml | 24 + 11 files changed, 1311 insertions(+), 25 deletions(-) create mode 100644 test-files/golden-tests/javadoc/copydoc/template-arguments.adoc create mode 100644 test-files/golden-tests/javadoc/copydoc/template-arguments.cpp create mode 100644 test-files/golden-tests/javadoc/copydoc/template-arguments.html create mode 100644 test-files/golden-tests/javadoc/copydoc/template-arguments.xml create mode 100644 test-files/golden-tests/javadoc/ref/punctuation.adoc create mode 100644 test-files/golden-tests/javadoc/ref/punctuation.cpp create mode 100644 test-files/golden-tests/javadoc/ref/punctuation.html create mode 100644 test-files/golden-tests/javadoc/ref/punctuation.xml diff --git a/mrdocs.rnc b/mrdocs.rnc index 15a23e1c5..c10ff2d2c 100644 --- a/mrdocs.rnc +++ b/mrdocs.rnc @@ -37,7 +37,7 @@ grammar attribute is-anonymous { "1" }?, Javadoc?, element using-directive { ID } *, - Scope + Scope* } #--------------------------------------------- @@ -153,7 +153,8 @@ grammar Name, ID, Location *, - TypeInfo + TypeInfo, + Javadoc ? } | element typedef { diff --git a/src/lib/AST/ParseJavadoc.cpp b/src/lib/AST/ParseJavadoc.cpp index aada63079..b8bd81857 100644 --- a/src/lib/AST/ParseJavadoc.cpp +++ b/src/lib/AST/ParseJavadoc.cpp @@ -1089,14 +1089,6 @@ fixReference(std::string& ref) return text; }; - // If the ref is only "operator", the next text comment - // might contain a simple operator name/type, or a - // full operator overload. - // In this case, we need to include the next text comments - // until we find this operator identifier/type or until - // we find an unbalanced '('. - // Simply including the next text comment is enough - // for the next step. ParsedRef v; while (true) { @@ -1118,7 +1110,7 @@ fixReference(std::string& ref) continue; } - // The ref is fully parsed + // The ref is not fully parsed if (pres.ptr != last) { // The ref didn't consume all the text, so we need to diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index 11411e993..d7104dd0a 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -311,6 +311,33 @@ isDecayedEqual( { return isDecayedEqualImpl<false>(lhs, rhs, context, corpus); } + +bool +isDecayedEqual( + Polymorphic<TArg> const& lhs, + Polymorphic<TArg> const& rhs, + Info const& context, + CorpusImpl const& corpus) +{ + if (lhs->Kind != rhs->Kind) + { + return false; + } + if (lhs->isType()) + { + return isDecayedEqualImpl<true>( + get<TypeTArg>(lhs).Type, + get<TypeTArg>(rhs).Type, + context, corpus); + } + if (lhs->isNonType()) + { + return + trim(get<NonTypeTArg>(lhs).Value.Written) == + trim(get<NonTypeTArg>(rhs).Value.Written); + } + return false; +} } Expected<std::reference_wrapper<Info const>> @@ -353,7 +380,7 @@ lookupImpl(Self&& self, SymbolID const& contextId0, std::string_view name) } return std::cref(*info); } - Expected<ParsedRef> const expRef = parseRef(name); + auto const expRef = parse<ParsedRef>(name); if (!expRef) { return Unexpected(formatError("Failed to parse '{}'\n {}", name, expRef.error().reason())); @@ -494,7 +521,7 @@ lookupImpl( }; auto const highestMatchLevel = !checkParameters || !ref.HasFunctionParameters ? - MatchLevel::TemplateArgsSize : + MatchLevel::TemplateArgs : MatchLevel::Qualifiers; auto matchLevel = MatchLevel::None; Info const* res = nullptr; @@ -535,29 +562,41 @@ lookupImpl( } matchRes = MatchLevel::Name; - // Template arguments match - if constexpr (requires { M.Template; }) - { - std::optional<TemplateInfo> const& templateInfo = M.Template; - if (component.TemplateArguments.empty()) + // Template arguments size match + TemplateInfo const* templateInfo = [&]() -> TemplateInfo const* { + if constexpr (requires { M.Template; }) { - MRDOCS_CHECK_OR( - !templateInfo.has_value() || - templateInfo->Args.empty(), matchRes); + std::optional<TemplateInfo> const& OTI = M.Template; + MRDOCS_CHECK_OR(OTI, nullptr); + TemplateInfo const& TI = *OTI; + return &TI; } else { - MRDOCS_CHECK_OR( - templateInfo.has_value() && - templateInfo->Args.size() == component.TemplateArguments.size(), matchRes); + return nullptr; } + }(); + if (!templateInfo) + { + MRDOCS_CHECK_OR(!component.HasTemplateArguments, matchRes); } else { - MRDOCS_CHECK_OR(component.TemplateArguments.empty(), matchRes); + MRDOCS_CHECK_OR(templateInfo->Args.size() == component.TemplateArguments.size(), matchRes); } matchRes = MatchLevel::TemplateArgsSize; + if (templateInfo) + { + for (std::size_t i = 0; i < templateInfo->Args.size(); ++i) + { + auto& lhsType = templateInfo->Args[i]; + auto& rhsType = component.TemplateArguments[i]; + MRDOCS_CHECK_OR(isDecayedEqual(lhsType, rhsType, context, *this), matchRes); + } + } + matchRes = MatchLevel::TemplateArgs; + // Function parameters size match MRDOCS_CHECK_OR(checkParameters && ref.HasFunctionParameters, matchRes); MRDOCS_CHECK_OR(MInfoTy::isFunction(), matchRes); diff --git a/test-files/golden-tests/javadoc/copydoc/template-arguments.adoc b/test-files/golden-tests/javadoc/copydoc/template-arguments.adoc new file mode 100644 index 000000000..7b14abf94 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/template-arguments.adoc @@ -0,0 +1,367 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +=== Namespaces + +[cols=1] +|=== +| Name + +| <<A,`A`>> + +|=== + +[#A] +== A + + +=== Types + +[cols=2] +|=== +| Name +| Description + +| <<A-B-08,`B`>> +| Main class template for B. + +| <<A-B-09,`B<int>`>> +| Specialization of B for int. + +| <<A-B-0c,`B<int, 2>`>> +| Specialization of B for int with value 2. + +| <<A-C-05,`C`>> +| Main class template for C. + +| <<A-C-0f,`C<D, true>`>> +| Specialization of C for D with true. + +| <<A-C-0c,`C<int, true>`>> +| Specialization of C for int with true. + +| <<A-D,`D`>> +| Helper struct D. + +| <<A-BInt,`BInt`>> +| Specialization of B for int. + +| <<A-BInt2,`BInt2`>> +| Specialization of B for int with value 2. + +| <<A-B_t,`B_t`>> +| Main class template for B. + +| <<A-CDTrue,`CDTrue`>> +| Specialization of C for D with true. + +| <<A-CIntTrue,`CIntTrue`>> +| Specialization of C for D with true. + +| <<A-C_t,`C_t`>> +| Main class template for C. + +|=== + +[#A-BInt] +== <<A,A>>::BInt + + +Specialization of B for int. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +using BInt = <<A-B-09,B>><int>; +---- + +[#A-BInt2] +== <<A,A>>::BInt2 + + +Specialization of B for int with value 2. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +using BInt2 = <<A-B-0c,B>><int, 2>; +---- + +[#A-B_t] +== <<A,A>>::B_t + + +Main class template for B. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template< + class T, + int I> +using B_t = <<A-B-08,B>><T, I>; +---- + +=== Template Parameters + + +|=== +| Name | Description + +| *T* +| The type parameter. + +|=== + +[#A-CDTrue] +== <<A,A>>::CDTrue + + +Specialization of C for D with true. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +using CDTrue = <<A-C-0f,C>><<<A-D,D>>, true>; +---- + +[#A-CIntTrue] +== <<A,A>>::CIntTrue + + +Specialization of C for D with true. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +using CIntTrue = <<A-C-0c,C>><int, true>; +---- + +[#A-C_t] +== <<A,A>>::C_t + + +Main class template for C. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template< + class T, + bool B> +using C_t = <<A-C-05,C>><T, B>; +---- + +=== Template Parameters + + +|=== +| Name | Description + +| *T* +| The type parameter. + +|=== + +[#A-B-08] +== <<A,A>>::B + + +Main class template for B. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template< + class T, + int = 0> +struct B; +---- + + + + +=== Template Parameters + + +|=== +| Name | Description + +| *T* +| The type parameter. + +| *int* +| The integer parameter with a default value of 0. + +|=== + +[#A-B-09] +== <<A,A>>::B<int> + + +Specialization of B for int. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<> +struct <<A-B-08,B>><int>; +---- + + + + +[#A-B-0c] +== <<A,A>>::B<int, 2> + + +Specialization of B for int with value 2. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<> +struct <<A-B-08,B>><int, 2>; +---- + + + + +[#A-C-05] +== <<A,A>>::C + + +Main class template for C. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template< + class T, + bool = false> +struct C; +---- + + + + +=== Template Parameters + + +|=== +| Name | Description + +| *T* +| The type parameter. + +| *bool* +| The boolean parameter with a default value of false. + +|=== + +[#A-C-0f] +== <<A,A>>::C<<<A-D,D>>, true> + + +Specialization of C for D with true. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<> +struct <<A-C-05,C>><<<A-D,D>>, true>; +---- + + + + +[#A-C-0c] +== <<A,A>>::C<int, true> + + +Specialization of C for int with true. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +template<> +struct <<A-C-05,C>><int, true>; +---- + + + + +[#A-D] +== <<A,A>>::D + + +Helper struct D. + +=== Synopsis + + +Declared in `<template‐arguments.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct D; +---- + + + + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/javadoc/copydoc/template-arguments.cpp b/test-files/golden-tests/javadoc/copydoc/template-arguments.cpp new file mode 100644 index 000000000..f749fddf9 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/template-arguments.cpp @@ -0,0 +1,61 @@ +namespace A { + /** Main class template for B. + + @tparam T The type parameter. + @tparam int The integer parameter with a default value of 0. + */ + template <class T, int = 0> + struct B; + + /// @copydoc B + template <class T, int I> + using B_t = B<T, I>; + + /** Specialization of B for int. + */ + template <> + struct B<int> {}; + + /// @copydoc B<int> + using BInt = B<int>; + + /** Specialization of B for int with value 2. + */ + template <> + struct B<int, 2> {}; + + /// @copydoc B<int, 2> + using BInt2 = B<int, 2>; + + /** Main class template for C. + + @tparam T The type parameter. + @tparam bool The boolean parameter with a default value of false. + */ + template <class T, bool = false> + struct C; + + /// @copydoc C + template <class T, bool B> + using C_t = C<T, B>; + + /** Specialization of C for int with true. + */ + template <> + struct C<int, true> {}; + + /// @copydoc C<int, true> + using CIntTrue = C<int, true>; + + /** Helper struct D. + */ + struct D; + + /** Specialization of C for D with true. + */ + template <> + struct C<D, true> {}; + + /// @copydoc C<D, true> + using CDTrue = C<D, true>; +} \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/copydoc/template-arguments.html b/test-files/golden-tests/javadoc/copydoc/template-arguments.html new file mode 100644 index 000000000..8a2176b3b --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/template-arguments.html @@ -0,0 +1,460 @@ +<html lang="en"> +<head> +<title>Reference</title> +</head> +<body> +<div> +<h1>Reference</h1> +<div> +<div> +<h2 id="index">Global namespace</h2> +</div> +<h2>Namespaces</h2> +<table style="table-layout: fixed; width: 100%;"> +<thead> +<tr> +<th>Name</th> +</tr> +</thead> +<tbody> +<tr> +<td><a href="#A"><code>A</code></a> </td> +</tr> +</tbody> +</table> +</div> +<div> +<div> +<h2 id="A">A</h2> +</div> +<h2>Types</h2> +<table style="table-layout: fixed; width: 100%;"> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><a href="#A-B-08"><code>B</code></a> </td><td><span><span>Main class template for B.</span></span> +</td> +</tr><tr> +<td><a href="#A-B-09"><code>B<int></code></a> </td><td><span><span>Specialization of B for int.</span></span> +</td> +</tr><tr> +<td><a href="#A-B-0c"><code>B<int, 2></code></a> </td><td><span><span>Specialization of B for int with value 2.</span></span> +</td> +</tr><tr> +<td><a href="#A-C-05"><code>C</code></a> </td><td><span><span>Main class template for C.</span></span> +</td> +</tr><tr> +<td><a href="#A-C-0f"><code>C<D, true></code></a> </td><td><span><span>Specialization of C for D with true.</span></span> +</td> +</tr><tr> +<td><a href="#A-C-0c"><code>C<int, true></code></a> </td><td><span><span>Specialization of C for int with true.</span></span> +</td> +</tr><tr> +<td><a href="#A-D"><code>D</code></a> </td><td><span><span>Helper struct D.</span></span> +</td> +</tr><tr> +<td><a href="#A-BInt"><code>BInt</code></a> </td><td><span><span>Specialization of B for int.</span></span> +</td> +</tr><tr> +<td><a href="#A-BInt2"><code>BInt2</code></a> </td><td><span><span>Specialization of B for int with value 2.</span></span> +</td> +</tr><tr> +<td><a href="#A-B_t"><code>B_t</code></a> </td><td><span><span>Main class template for B.</span></span> +</td> +</tr><tr> +<td><a href="#A-CDTrue"><code>CDTrue</code></a> </td><td><span><span>Specialization of C for D with true.</span></span> +</td> +</tr><tr> +<td><a href="#A-CIntTrue"><code>CIntTrue</code></a> </td><td><span><span>Specialization of C for D with true.</span></span> +</td> +</tr><tr> +<td><a href="#A-C_t"><code>C_t</code></a> </td><td><span><span>Main class template for C.</span></span> +</td> +</tr> +</tbody> +</table> +</div> +<div> +<div> +<h2 id="A-BInt"><a href="#A">A</a>::BInt</h2> +<div> +<span><span>Specialization of B for int.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +using BInt = <a href="#A-B-09">B</a><int>; +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="A-BInt2"><a href="#A">A</a>::BInt2</h2> +<div> +<span><span>Specialization of B for int with value 2.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +using BInt2 = <a href="#A-B-0c">B</a><int, 2>; +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="A-B_t"><a href="#A">A</a>::B_t</h2> +<div> +<span><span>Main class template for B.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template< + class T, + int I> +using B_t = <a href="#A-B-08">B</a><T, I>; +</code> +</pre> +</div> +<div> +<h3>Template Parameters</h3> +<table> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>T</strong></td> +<td><p><span>The type parameter.</span></p> +</td> +</tr> +</tbody> +</table> +</div> +</div> +<div> +<div> +<h2 id="A-CDTrue"><a href="#A">A</a>::CDTrue</h2> +<div> +<span><span>Specialization of C for D with true.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +using CDTrue = <a href="#A-C-0f">C</a><<a href="#A-D">D</a>, true>; +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="A-CIntTrue"><a href="#A">A</a>::CIntTrue</h2> +<div> +<span><span>Specialization of C for D with true.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +using CIntTrue = <a href="#A-C-0c">C</a><int, true>; +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="A-C_t"><a href="#A">A</a>::C_t</h2> +<div> +<span><span>Main class template for C.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template< + class T, + bool B> +using C_t = <a href="#A-C-05">C</a><T, B>; +</code> +</pre> +</div> +<div> +<h3>Template Parameters</h3> +<table> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>T</strong></td> +<td><p><span>The type parameter.</span></p> +</td> +</tr> +</tbody> +</table> +</div> +</div> +<div> +<div> +<h2 id="A-B-08"><a href="#A">A</a>::B</h2> +<div> +<span><span>Main class template for B.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template< + class T, + int = 0> +struct B; +</code> +</pre> +</div> + + +<div> +<h3>Template Parameters</h3> +<table> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>T</strong></td> +<td><p><span>The type parameter.</span></p> +</td> +</tr> +<tr> +<td><strong>int</strong></td> +<td><p><span>The integer parameter with a default value of 0.</span></p> +</td> +</tr> +</tbody> +</table> +</div> +</div> +<div> +<div> +<h2 id="A-B-09"><a href="#A">A</a>::B<int></h2> +<div> +<span><span>Specialization of B for int.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template<> +struct <a href="#A-B-08">B</a><int>; +</code> +</pre> +</div> + + +</div> +<div> +<div> +<h2 id="A-B-0c"><a href="#A">A</a>::B<int, 2></h2> +<div> +<span><span>Specialization of B for int with value 2.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template<> +struct <a href="#A-B-08">B</a><int, 2>; +</code> +</pre> +</div> + + +</div> +<div> +<div> +<h2 id="A-C-05"><a href="#A">A</a>::C</h2> +<div> +<span><span>Main class template for C.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template< + class T, + bool = false> +struct C; +</code> +</pre> +</div> + + +<div> +<h3>Template Parameters</h3> +<table> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><strong>T</strong></td> +<td><p><span>The type parameter.</span></p> +</td> +</tr> +<tr> +<td><strong>bool</strong></td> +<td><p><span>The boolean parameter with a default value of false.</span></p> +</td> +</tr> +</tbody> +</table> +</div> +</div> +<div> +<div> +<h2 id="A-C-0f"><a href="#A">A</a>::C<<a href="#A-D">D</a>, true></h2> +<div> +<span><span>Specialization of C for D with true.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template<> +struct <a href="#A-C-05">C</a><<a href="#A-D">D</a>, true>; +</code> +</pre> +</div> + + +</div> +<div> +<div> +<h2 id="A-C-0c"><a href="#A">A</a>::C<int, true></h2> +<div> +<span><span>Specialization of C for int with true.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +template<> +struct <a href="#A-C-05">C</a><int, true>; +</code> +</pre> +</div> + + +</div> +<div> +<div> +<h2 id="A-D"><a href="#A">A</a>::D</h2> +<div> +<span><span>Helper struct D.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><template-arguments.cpp></code></div> +<pre> +<code class="source-code cpp"> +struct D; +</code> +</pre> +</div> + + +</div> + +</div> +<div> +<h4>Created with <a href="https://www.mrdocs.com">MrDocs</a></h4> +</div> +</body> +</html> \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/copydoc/template-arguments.xml b/test-files/golden-tests/javadoc/copydoc/template-arguments.xml new file mode 100644 index 000000000..8b092f666 --- /dev/null +++ b/test-files/golden-tests/javadoc/copydoc/template-arguments.xml @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mrdocs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://github.com/cppalliance/mrdocs/raw/develop/mrdocs.rnc"> +<namespace id="//////////////////////////8="> + <namespace name="A" id="jQQu/8mLNzRQvGtbkKMwwloVDpw="> + <namespace-alias name="BInt" id="knD7LwHco1Rc3UN13fDrTDK0Fg4="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="20"/> + <type id="n2ekv7/9IraylJIibujZeExrML4=" name="B<int>"/> + <doc> + <brief> + <text>Specialization of B for int.</text> + </brief> + </doc> + </namespace-alias> + <namespace-alias name="BInt2" id="mAmfKzQQ8Mg78wrSV5C5vqhB38M="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="28"/> + <type id="zWh7HJU4uEOW6SQu1/X6/fBkFq0=" name="B<int, 2>"/> + <doc> + <brief> + <text>Specialization of B for int with value 2.</text> + </brief> + </doc> + </namespace-alias> + <template> + <tparam name="T" class="type"/> + <tparam name="I" class="non-type" type="int"/> + <namespace-alias name="B_t" id="40FFJWRtg8LikFmMYkFhi3taM7w="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="11"/> + <type id="jJtr0x+P2kzEfxAlp7Hjo5akJho=" name="B<T, I>"/> + <doc> + <brief> + <text>Main class template for B.</text> + </brief> + <tparam name="T"> + <text>The type parameter. </text> + </tparam> + </doc> + </namespace-alias> + </template> + <namespace-alias name="CDTrue" id="EFfBXjCvLk0J5w7aUT/GZ5D4Zww="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="60"/> + <type id="+AYNfv3bBiQwa6z9A60U/F7dvXE=" name="C<D, true>"/> + <doc> + <brief> + <text>Specialization of C for D with true.</text> + </brief> + </doc> + </namespace-alias> + <namespace-alias name="CIntTrue" id="9GvIXSW1TfhlI8qPuV4xPDXT5+A="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="48"/> + <type id="w5ssdgUOfKNpDbPFUfSYesxFAc8=" name="C<int, true>"/> + <doc> + <brief> + <text>Specialization of C for D with true.</text> + </brief> + </doc> + </namespace-alias> + <template> + <tparam name="T" class="type"/> + <tparam name="B" class="non-type" type="bool"/> + <namespace-alias name="C_t" id="N9KZXiVz/IMBW2CcGYh+8cvUeFQ="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="39"/> + <type id="WjzMcZfRtncsiJEvL1/LzUes/GM=" name="C<T, B>"/> + <doc> + <brief> + <text>Main class template for C.</text> + </brief> + <tparam name="T"> + <text>The type parameter. </text> + </tparam> + </doc> + </namespace-alias> + </template> + <template> + <tparam name="T" class="type"/> + <tparam class="non-type" type="int" default="0"/> + <struct name="B" id="jJtr0x+P2kzEfxAlp7Hjo5akJho="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="7"/> + <doc> + <brief> + <text>Main class template for B.</text> + </brief> + <tparam name="T"> + <text>The type parameter. </text> + </tparam> + <tparam name="int"> + <text>The integer parameter with a default value of 0.</text> + </tparam> + </doc> + </struct> + </template> + <template class="explicit" id="jJtr0x+P2kzEfxAlp7Hjo5akJho="> + <targ class="type" type="int"/> + <struct name="B" id="n2ekv7/9IraylJIibujZeExrML4="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="17" class="def"/> + <doc> + <brief> + <text>Specialization of B for int.</text> + </brief> + </doc> + </struct> + </template> + <template class="explicit" id="jJtr0x+P2kzEfxAlp7Hjo5akJho="> + <targ class="type" type="int"/> + <targ class="non-type" value="2"/> + <struct name="B" id="zWh7HJU4uEOW6SQu1/X6/fBkFq0="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="25" class="def"/> + <doc> + <brief> + <text>Specialization of B for int with value 2.</text> + </brief> + </doc> + </struct> + </template> + <template> + <tparam name="T" class="type"/> + <tparam class="non-type" type="bool" default="false"/> + <struct name="C" id="WjzMcZfRtncsiJEvL1/LzUes/GM="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="35"/> + <doc> + <brief> + <text>Main class template for C.</text> + </brief> + <tparam name="T"> + <text>The type parameter. </text> + </tparam> + <tparam name="bool"> + <text>The boolean parameter with a default value of false.</text> + </tparam> + </doc> + </struct> + </template> + <template class="explicit" id="WjzMcZfRtncsiJEvL1/LzUes/GM="> + <targ class="type" type="D"/> + <targ class="non-type" value="true"/> + <struct name="C" id="+AYNfv3bBiQwa6z9A60U/F7dvXE="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="57" class="def"/> + <doc> + <brief> + <text>Specialization of C for D with true.</text> + </brief> + </doc> + </struct> + </template> + <template class="explicit" id="WjzMcZfRtncsiJEvL1/LzUes/GM="> + <targ class="type" type="int"/> + <targ class="non-type" value="true"/> + <struct name="C" id="w5ssdgUOfKNpDbPFUfSYesxFAc8="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="45" class="def"/> + <doc> + <brief> + <text>Specialization of C for int with true.</text> + </brief> + </doc> + </struct> + </template> + <struct name="D" id="/TldxA72JGXtufh5RbJgxLW2mwU="> + <file short-path="template-arguments.cpp" source-path="template-arguments.cpp" line="52"/> + <doc> + <brief> + <text>Helper struct D.</text> + </brief> + </doc> + </struct> + </namespace> +</namespace> +</mrdocs> diff --git a/test-files/golden-tests/javadoc/ref/punctuation.adoc b/test-files/golden-tests/javadoc/ref/punctuation.adoc new file mode 100644 index 000000000..421c60b5e --- /dev/null +++ b/test-files/golden-tests/javadoc/ref/punctuation.adoc @@ -0,0 +1,75 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +=== Functions + +[cols=2] +|=== +| Name +| Description + +| <<f0,`f0`>> +| + +| <<f1,`f1`>> +| + +| <<f2,`f2`>> +| See xref:#f0[f0], xref:#f1[f1]. + +|=== + +[#f0] +== f0 + + +=== Synopsis + + +Declared in `<punctuation.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f0(); +---- + +[#f1] +== f1 + + +=== Synopsis + + +Declared in `<punctuation.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f1(); +---- + +[#f2] +== f2 + + +See xref:#f0[f0], xref:#f1[f1]. + +=== Synopsis + + +Declared in `<punctuation.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +f2(); +---- + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/javadoc/ref/punctuation.cpp b/test-files/golden-tests/javadoc/ref/punctuation.cpp new file mode 100644 index 000000000..934603a32 --- /dev/null +++ b/test-files/golden-tests/javadoc/ref/punctuation.cpp @@ -0,0 +1,9 @@ +void f0(); + +void f1(); + +/** + See @ref f0, @ref f1. +*/ +void f2(); + diff --git a/test-files/golden-tests/javadoc/ref/punctuation.html b/test-files/golden-tests/javadoc/ref/punctuation.html new file mode 100644 index 000000000..114387e11 --- /dev/null +++ b/test-files/golden-tests/javadoc/ref/punctuation.html @@ -0,0 +1,91 @@ +<html lang="en"> +<head> +<title>Reference</title> +</head> +<body> +<div> +<h1>Reference</h1> +<div> +<div> +<h2 id="index">Global namespace</h2> +</div> +<h2>Functions</h2> +<table style="table-layout: fixed; width: 100%;"> +<thead> +<tr> +<th>Name</th> +<th>Description</th> +</tr> +</thead> +<tbody> +<tr> +<td><a href="#f0"><code>f0</code></a> </td><td></td> +</tr><tr> +<td><a href="#f1"><code>f1</code></a> </td><td></td> +</tr><tr> +<td><a href="#f2"><code>f2</code></a> </td><td><span><span>See </span><a href="#f0"><code>f0</code></a> <span>, </span><a href="#f1"><code>f1</code></a> <span>.</span></span> +</td> +</tr> +</tbody> +</table> +</div> +<div> +<div> +<h2 id="f0">f0</h2> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><punctuation.cpp></code></div> +<pre> +<code class="source-code cpp"> +void +f0(); +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="f1">f1</h2> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><punctuation.cpp></code></div> +<pre> +<code class="source-code cpp"> +void +f1(); +</code> +</pre> +</div> +</div> +<div> +<div> +<h2 id="f2">f2</h2> +<div> +<span><span>See </span><a href="#f0"><code>f0</code></a> <span>, </span><a href="#f1"><code>f1</code></a> <span>.</span></span> + + +</div> +</div> +<div> +<h3>Synopsis</h3> +<div> +Declared in <code><punctuation.cpp></code></div> +<pre> +<code class="source-code cpp"> +void +f2(); +</code> +</pre> +</div> +</div> + +</div> +<div> +<h4>Created with <a href="https://www.mrdocs.com">MrDocs</a></h4> +</div> +</body> +</html> \ No newline at end of file diff --git a/test-files/golden-tests/javadoc/ref/punctuation.xml b/test-files/golden-tests/javadoc/ref/punctuation.xml new file mode 100644 index 000000000..6b9f41ff6 --- /dev/null +++ b/test-files/golden-tests/javadoc/ref/punctuation.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mrdocs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="https://github.com/cppalliance/mrdocs/raw/develop/mrdocs.rnc"> +<namespace id="//////////////////////////8="> + <function name="f0" id="e1UQQek5v3C9OClW5cGf57XvwQo="> + <file short-path="punctuation.cpp" source-path="punctuation.cpp" line="1"/> + </function> + <function name="f1" id="CnO51rIKTzfiVKHkR3TdPa0eo+8="> + <file short-path="punctuation.cpp" source-path="punctuation.cpp" line="3"/> + </function> + <function name="f2" id="0MJUv5yGFR9nXWFLeYc+rjOY+iM="> + <file short-path="punctuation.cpp" source-path="punctuation.cpp" line="8"/> + <doc> + <brief> + <text>See </text> + <reference id="e1UQQek5v3C9OClW5cGf57XvwQo=">f0</reference> + <text>, </text> + <reference id="CnO51rIKTzfiVKHkR3TdPa0eo+8=">f1</reference> + <text>.</text> + </brief> + </doc> + </function> +</namespace> +</mrdocs>