diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4b22f7789..ca13794b85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -640,6 +640,8 @@ jobs: "html" ) + cp example/external/url/mrdocs.yml boost/libs/url/doc/mrdocs.yml + # Generate the demos for each variant and generator for variant in single multi; do for generator in "${generators[@]}"; do diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f388c70fd..fd2f4da889 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,7 @@ if (WIN32) /W4 # enable all warnings /MP # multi-processor compilation /EHs # C++ Exception handling + $<$:/Oy-> # Disable frame pointer omission ) endif() endif () @@ -388,7 +389,7 @@ if (MRDOCS_BUILD_TESTS) --action=test "${PROJECT_SOURCE_DIR}/test-files/golden-tests" "--addons=${CMAKE_SOURCE_DIR}/share/mrdocs/addons" - --generate=${testgenerator} + --generator=${testgenerator} "--stdlib-includes=${LIBCXX_DIR}" "--stdlib-includes=${STDLIB_INCLUDE_DIR}" "--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs" @@ -403,7 +404,7 @@ if (MRDOCS_BUILD_TESTS) --action=${action} "${PROJECT_SOURCE_DIR}/test-files/golden-tests" "--addons=${CMAKE_SOURCE_DIR}/share/mrdocs/addons" - --generate=${testgenerator} + --generator=${testgenerator} "--stdlib-includes=${LIBCXX_DIR}" "--stdlib-includes=${STDLIB_INCLUDE_DIR}" "--libc-includes=${CMAKE_SOURCE_DIR}/share/mrdocs/headers/libc-stubs" diff --git a/docs/modules/ROOT/pages/generators.adoc b/docs/modules/ROOT/pages/generators.adoc index 055392e1b4..1592c45d35 100644 --- a/docs/modules/ROOT/pages/generators.adoc +++ b/docs/modules/ROOT/pages/generators.adoc @@ -157,9 +157,25 @@ The `Symbol` object represents a symbol extracted from the source code.The symbo | `string` | The access level of the symbol. (e.g., `public`, `protected`, `private`) -| `implicit` +| `extraction` | `string` -| Whether the symbol was implicitly extracted as a dependency. +| The extraction mode of the symbol according to the specified filters. (e.g., `regular`, `see-below`, `implementation-defined`, `dependency`) + +| `isRegular` +| `bool` +| Whether the symbol extraction mode is `regular`. + +| `isSeeBelow` +| `bool` +| Whether the symbol extraction mode is `see-below`. + +| `isImplementationDefined` +| `bool` +| Whether the symbol extraction mode is `implementation-defined`. + +| `isDependency` +| `bool` +| Whether the symbol extraction mode is `dependency`. | `parents` | `<>` @@ -385,6 +401,10 @@ When the symbol kind is `function`, the symbol object has the following addition | `requires` | `string` | The `requires` expression of the function. + +| `attributes` +| `string[]` +| The attributes of the function. |=== When the symbol kind is `typedef`, the symbol object has the following additional properties: @@ -479,6 +499,10 @@ When the symbol kind is `field` (i.e. non-static data members), the symbol objec | `bitfieldWidth` | `string` | The width of the bitfield. + +| `attributes` +| `string[]` +| The attributes of the field. |=== When the symbol kind is `friend`, the symbol object has the following additional properties: diff --git a/docs/mrdocs.schema.json b/docs/mrdocs.schema.json index 8c1529a9dd..dde6f3af09 100644 --- a/docs/mrdocs.schema.json +++ b/docs/mrdocs.schema.json @@ -8,14 +8,10 @@ "type": "string" }, "anonymous-namespaces": { - "default": "always", + "default": true, "description": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.", - "enum": [ - "always", - "dependency", - "never" - ], - "title": "Extraction policy for anonymous namespaces" + "title": "Extraction policy for anonymous namespaces", + "type": "boolean" }, "base-url": { "default": "", @@ -44,52 +40,62 @@ "title": "Additional defines passed to the compiler", "type": "array" }, - "detect-sfinae": { - "default": true, - "description": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement.", - "title": "Detect SFINAE expressions", - "type": "boolean" - }, "embedded": { "default": false, - "title": "Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. This option is useful for producing documents that can be inserted into an external template.", + "description": "Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. This option is useful for producing documents that can be inserted into an external template.", + "title": "Output an embeddable document", "type": "boolean" }, - "filters": { - "properties": { - "symbols": { - "description": "Symbol filters. Symbols that match these filters are extracted. The filters are applied to the fully qualified name of the symbol.", - "properties": { - "exclude": { - "default": [], - "description": "Specifies symbol exclusion patterns. Symbols that match these patterns are not extracted. The patterns are applied to the fully qualified name of the symbol.", - "items": { - "type": "string" - }, - "title": "Specifies symbol exclusion patterns", - "type": "array" - }, - "include": { - "default": [], - "description": "Specifies symbol inclusion patterns. Symbols that match these patterns are extracted. The patterns are applied to the fully qualified name of the symbol.", - "items": { - "type": "string" - }, - "title": "Specifies symbol inclusion patterns", - "type": "array" - } - }, - "required": [], - "title": "Symbol filters", - "type": "object" - } + "exclude": { + "default": [], + "description": "Symbols defined in files in these directories are not extracted even if they are in the list of include directories. When relative, the paths are relative to the directory of the mrdocs configuration file. For instance, \"include/experimental\" will exclude all files in the directory `/include/experimental`.", + "items": { + "type": "string" + }, + "title": "Input directories to exclude", + "type": "array" + }, + "exclude-patterns": { + "default": [], + "description": "File patterns to exclude. Files that match these patterns are not extracted even if they are in the list of include directories. The patterns are relative to the configuration file. A single * will match all files in the directory. Double ** will match all files in the directory and its subdirectories.", + "items": { + "type": "string" + }, + "title": "File patterns to exclude", + "type": "array" + }, + "exclude-symbols": { + "default": [], + "description": "A symbol that matches one of these patterns is not extracted even if whitelisted by \"include-symbols\". See the documentation for \"include-symbols\" for the pattern syntax.", + "items": { + "type": "string" + }, + "title": "Symbol patterns to exclude", + "type": "array" + }, + "file-patterns": { + "default": [ + "*.hpp", + "*.h", + "*.hh", + "*.ipp", + "*.inc", + "*.cpp", + "*.cc", + "*.cxx", + "*.c", + "*.hxx" + ], + "description": "File patterns to include. Only the files that match these patterns are extracted. The patterns are relative to the input directories.", + "items": { + "type": "string" }, - "required": [], - "title": "Filters", - "type": "object" + "title": "File patterns to include", + "type": "array" }, - "generate": { + "generator": { "default": "adoc", + "description": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The generator can create different types of documentation such as HTML, XML, and AsciiDoc.", "enum": [ "adoc", "html", @@ -111,32 +117,21 @@ }, "implementation-defined": { "default": [], - "description": "Namespaces for symbols rendered as \"implementation-defined\". Symbols in these are rendered as \"implementation-defined\" in the documentation. This option is used to exclude symbols from the documentation that are considered part of the private API of the project. An \"implementation-defined\" symbol has no documentation page in the output. If any other symbol refers to it, the reference is rendered as \"implementation-defined\".", + "description": "Symbols that match one of these filters are tagged as \"implementation-defined\" in the documentation, and so do symbols in scopes tagged as \"implementation-defined\". This option is used to exclude symbols from the documentation that are considered part of the private API of the project. An \"implementation-defined\" symbol has no documentation page in the output. If any other symbol refers to it, the reference is rendered as \"implementation-defined\". See the documentation for \"include-symbol\" for the pattern syntax.", "items": { "type": "string" }, - "title": "Namespaces for symbols rendered as \"implementation-defined\"", + "title": "Symbols rendered as \"implementation-defined\"", "type": "array" }, - "inaccessible-bases": { - "default": "always", - "description": "Determine whether inaccessible base classes should be extracted. When set to `always`, inaccessible base classes are always extracted. When set to `dependency`, inaccessible base classes are extracted only if they are referenced by the source code. When set to `never`, inaccessible base classes are never extracted.", - "enum": [ - "always", - "dependency", - "never" - ], - "title": "Extraction policy for inaccessible base classes" - }, - "inaccessible-members": { - "default": "always", - "description": "Determine whether inaccessible members, such as private members of records, should be extracted. When set to `always`, inaccessible members are always extracted. When set to `dependency`, inaccessible members are extracted only if they are referenced by the source code. When set to `never`, inaccessible members are never extracted.", - "enum": [ - "always", - "dependency", - "never" - ], - "title": "Extraction policy for inaccessible members" + "include-symbols": { + "default": [], + "description": "If any patterns are defined here, only symbols that match one of these patterns are extracted. The patterns are applied to the fully qualified name of the symbol without any leading \"::\". A single \"*\" will match all symbols in the namespace. Double \"**\" will match all symbols in the namespace and its subnamespaces. The patterns also support \"?\" for any chars, \"[]\" for charsets, \"[^]\" for inverted charsets, and \"{,...}\" for alternatives.", + "items": { + "type": "string" + }, + "title": "Symbol patterns to include", + "type": "array" }, "includes": { "default": [], @@ -148,30 +143,15 @@ "type": "array" }, "input": { - "description": "Include files to extract. Only the files listed in this option are extracted. The paths are relative to the mrdocs configuration file.", - "properties": { - "file-patterns": { - "default": [], - "description": "File patterns to include. Only the files that match these patterns are extracted. The patterns are relative to the input directories.", - "items": { - "type": "string" - }, - "title": "File patterns to include", - "type": "array" - }, - "include": { - "default": [], - "description": "Input directories to include. Only the files in these directories are extracted. The paths are relative to the mrdocs configuration file.", - "items": { - "type": "string" - }, - "title": "Input directories to include", - "type": "array" - } + "default": [ + "/." + ], + "description": "Input directories to extract. Only symbols defined in files in these directories are extracted. The paths are relative to the mrdocs configuration file.", + "items": { + "type": "string" }, - "required": [], - "title": "Include files to extract", - "type": "object" + "title": "Input directories to extract symbols from", + "type": "array" }, "legible-names": { "default": true, @@ -202,15 +182,23 @@ "title": "Directory or file for generating output", "type": "string" }, - "referenced-declarations": { - "default": "dependency", - "description": "Determine whether external declarations should be extracted when they are referenced in the source code. If this option is not `never`, a second pass happens in the extraction process to extract dependencies in the Corpus. When set to `always`, external declarations are always extracted. When set to `dependency`, external declarations are extracted only if they are referenced by the source code. When set to `never`, external declarations are never extracted.", - "enum": [ - "always", - "dependency", - "never" - ], - "title": "Extraction policy for references to external declarations" + "private-bases": { + "default": true, + "description": "Determine whether private base classes should be extracted", + "title": "Extraction policy for private base classes", + "type": "boolean" + }, + "private-members": { + "default": false, + "description": "Determine whether private class members should be extracted", + "title": "Extraction policy for private class members", + "type": "boolean" + }, + "recursive": { + "default": true, + "description": "Recursively include files. When set to true, MrDocs includes files in subdirectories of the input directories. When set to false, MrDocs includes only the files in the input directories.", + "title": "Recursively include files from \"input\" paths", + "type": "boolean" }, "report": { "default": 1, @@ -222,15 +210,22 @@ }, "see-below": { "default": [], - "description": "Namespaces for symbols rendered as \"see-below\". Symbols in these namespaces are rendered as \"see-below\" in the documentation. This option is used to exclude details about symbols from the documentation that are considered part of the private API of the project. In the documentation page for this symbol, the synopsis of the implementation of a \"see-below\" symbol is rendered as \"see-below\". When the symbol is a scope (such as a namespace or record), its members are not listed. The rest of the documentation is rendered as usual.", + "description": "Symbols that match one of these filters are tagged as \"see-below\" in the documentation, and so do symbols in scopes tagged as \"see-below\". This option is used to remove details about symbols that are considered part of the private API of the project. In the documentation page for this symbol, the synopsis of the implementation is rendered as \"see-below\" and members of scopes (such as a namespace or record) are not listed. The rest of the documentation is rendered as usual. See the documentation for \"include-symbol\" for the pattern syntax.", "items": { "type": "string" }, - "title": "Namespaces for symbols rendered as \"see-below\"", + "title": "Symbols rendered as \"see-below\"", "type": "array" }, + "sfinae": { + "default": true, + "description": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement.", + "title": "Detect and reduce SFINAE expressions", + "type": "boolean" + }, "source-root": { "default": "", + "description": "Path to the root directory of the source code. This path is used as a default for input files and a base for relative paths formed from absolute paths.", "title": "Path to the root directory of the source code", "type": "string" }, diff --git a/docs/mrdocs.yml b/docs/mrdocs.yml index afcf67fbe3..bcbe509ffe 100644 --- a/docs/mrdocs.yml +++ b/docs/mrdocs.yml @@ -3,5 +3,13 @@ verbose: true source-root: .. +input: + - ../include +file-patterns: + - '*.hpp' +include-symbols: + - 'clang::mrdocs::**' +implementation-defined: + - 'clang::mrdocs::detail' multipage: false -generate: adoc +generator: adoc diff --git a/docs/website/render.js b/docs/website/render.js index 31952f65f2..0d07db0406 100644 --- a/docs/website/render.js +++ b/docs/website/render.js @@ -74,7 +74,7 @@ target_compile_features(${sourceBasename} PRIVATE cxx_std_23) mrdocsInput, `--output=${mrdocsOutput}`, '--multipage=true', - '--generate=html', + '--generator=html', '--embedded=true', ]; const command = args.join(' '); diff --git a/docs/website/snippets/mrdocs.yml b/docs/website/snippets/mrdocs.yml index e7067b0574..255a15647f 100644 --- a/docs/website/snippets/mrdocs.yml +++ b/docs/website/snippets/mrdocs.yml @@ -1,4 +1,3 @@ source-root: . input: - include: - - . + - . diff --git a/example/external/url/mrdocs.yml b/example/external/url/mrdocs.yml new file mode 100644 index 0000000000..7e8603de00 --- /dev/null +++ b/example/external/url/mrdocs.yml @@ -0,0 +1,63 @@ +# Input +source-root: .. +# Directories that contain documented source files +input: + - ../include +# Patterns to filter out the source-files in the directories +file-patterns: + - '*.hpp' + +# Filters +include-symbols: + - 'boost::urls::**' +exclude-symbols: +implementation-defined: + - 'boost::urls::detail' + - 'boost::urls::**::detail' + - 'boost::urls::*_unsafe' + - 'boost::urls::*_t' + - 'boost::urls::make_error_code' + - 'boost::urls::make_void' + - 'boost::urls::implementation_defined' + - 'boost::urls::grammar::implementation_defined' + - 'boost::urls::string_token::implementation_defined' + - 'boost::urls::grammar::*_t' + - 'boost::urls::grammar::make_error_*' + - 'boost::urls::grammar::operator_*' + - 'boost::urls::string_token::*_t' +see-below: + - 'boost::urls::see_below' + - 'boost::urls::grammar::see_below' + - 'boost::urls::string_token::see_below' + +# Generator +generate: adoc +base-url: https://www.github.com/boostorg/url/blob/develop/include/ # boost/url/url_view.hpp + +# Style +verbose: true +multipage: true + +# The target for MrDocs simply includes all symbols defined in all +# headers with the appropriate compilation options. +# Nothing else should be included in the MrDocs configuration or +# would be useful to MrDocs. +# +# This single source file not only includes all symbols (the source +# files do not collectively include all headers) but also makes MrDocs +# run much faster than relying on the entire library. +# +# The time to extract the declarations went from ~8m6s to ~3s in our +# experiments: a 162x speedup while including all symbols! +# +# In practice, this special target is simply emulating the +# default behavior of the standardese tool with MrDocs, which +# requires the user to clearly specify the targets via the +# compilation database. +# +# The BOOST_URL_MRDOCS_BUILD=ON is the only option we usually need +# here. +# The other options are set just to ensure other targets are +# ignored even if these options are set as ON in the cache. +# +cmake: '-D BOOST_URL_MRDOCS_BUILD=ON -D CMAKE_CXX_STANDARD=20 -D BOOST_URL_BUILD_FUZZERS=OFF -D BOOST_URL_BUILD_EXAMPLES=OFF -D BOOST_URL_BUILD_TESTS=OFF -D BUILD_TESTING=OFF' diff --git a/include/mrdocs/Config.hpp b/include/mrdocs/Config.hpp index c020ddde64..221c0b45f7 100644 --- a/include/mrdocs/Config.hpp +++ b/include/mrdocs/Config.hpp @@ -120,7 +120,8 @@ class MRDOCS_DECL This string will always be native style and have a trailing directory separator. */ - std::string configDir; + std::string + configDir() const; /** Full path to the mrdocs root directory diff --git a/include/mrdocs/Config/ReferenceDirectories.hpp b/include/mrdocs/Config/ReferenceDirectories.hpp index 4660ead191..2e8e616652 100644 --- a/include/mrdocs/Config/ReferenceDirectories.hpp +++ b/include/mrdocs/Config/ReferenceDirectories.hpp @@ -18,9 +18,15 @@ namespace clang { namespace mrdocs { /** Reference directories used to resolve paths + + These are the main reference directories used to resolve paths in the + application. + + All other reference directories come directly from the + configuration file. */ -struct ReferenceDirectories { - std::string configDir; +struct ReferenceDirectories +{ std::string cwd; std::string mrdocsRoot; }; diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index b4bf5a605c..8adbd7f1f8 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -21,9 +22,9 @@ #include #include #include +#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** The collection of declarations in extracted form. */ @@ -160,6 +161,32 @@ class MRDOCS_VISIBLE } } + /** Visit the members of specified Info in a stable order. + + @param I The Info to visit. + @param info The Info to visit. + @param f The function to invoke. + @param args The arguments to pass to the function. + */ + template + void + orderedTraverse( + T const& I, F&& f, Args&&... args) const + { + std::vector members(I.Members.begin(), I.Members.end()); + std::stable_sort(members.begin(), members.end(), [this](SymbolID const& lhs, SymbolID const& rhs) + { + auto const& lhsInfo = get(lhs); + auto const& rhsInfo = get(rhs); + return lhsInfo < rhsInfo; + }); + for (auto const& id : members) + { + visit(get(id), std::forward(f), + std::forward(args)...); + } + } + /** Visit the member overloads of specified ScopeInfo. This function iterates the members of the @@ -184,6 +211,14 @@ class MRDOCS_VISIBLE F&& f, Args&&... args) const; + /** Visit the member overloads of specified ScopeInfo in stable order + */ + template + void orderedTraverseOverloads( + ScopeInfo const& S, + F&& f, + Args&&... args) const; + //-------------------------------------------- /** Return the fully qualified name of the specified Info. @@ -238,25 +273,20 @@ get( template void -Corpus:: -traverseOverloads( +traverseOverloadsImpl( + Corpus const& c, + std::vector const& members0, ScopeInfo const& S, - F&& f, Args&&... args) const + F&& f, Args&&... args) { - MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); - for(const SymbolID& id : S.Members) + for(const SymbolID& id : members0) { - const Info& member = get(id); + const Info& member = c.get(id); const auto& members = S.Lookups.at(member.Name); auto first_func = std::ranges::find_if( - members, [this](const SymbolID& elem) + members, [&c](const SymbolID& elem) { - #if 0 - const Info& I = get(elem); - return I.isFunction() || I.isGuide(); - #else - return get(elem).isFunction(); - #endif + return c.get(elem).isFunction(); }); bool const nonOverloadedFunction = members.size() == 1; bool const notFunction = first_func == members.end(); @@ -278,6 +308,40 @@ traverseOverloads( } } +template +void +Corpus:: +traverseOverloads( + ScopeInfo const& S, + F&& f, Args&&... args) const +{ + MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); + return traverseOverloadsImpl( + *this, S.Members, S, std::forward(f), std::forward(args)...); + +} + +template +void +Corpus:: +orderedTraverseOverloads( + ScopeInfo const& S, + F&& f, + Args&&... args) const +{ + MRDOCS_ASSERT(S.Members.empty() == S.Lookups.empty()); + std::vector members(S.Members.begin(), S.Members.end()); + std::stable_sort(members.begin(), members.end(), [this](SymbolID const& lhs, SymbolID const& rhs) + { + auto const& lhsInfo = get(lhs); + auto const& rhsInfo = get(rhs); + return lhsInfo < rhsInfo; + }); + return traverseOverloadsImpl( + *this, members, S, std::forward(f), std::forward(args)...); +} + + class Corpus::iterator { const Corpus* corpus_; @@ -351,7 +415,6 @@ MRDOCS_DECL std::vector getParents(Corpus const& C, Info const& I); -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/ExtractionMode.hpp b/include/mrdocs/Metadata/ExtractionMode.hpp new file mode 100644 index 0000000000..4eb8f088ad --- /dev/null +++ b/include/mrdocs/Metadata/ExtractionMode.hpp @@ -0,0 +1,114 @@ +// +// This is a derivative work. originally part of the LLVM Project. +// Licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_EXTRACTIONMODE_HPP +#define MRDOCS_API_METADATA_EXTRACTIONMODE_HPP + +#include +#include +#include + +namespace clang::mrdocs { + +/** Determine why a symbol is extracted + + The enum constants are ordered by specificity, with + the least specific at the beginning and the most + specific at the end. + + */ +enum class ExtractionMode +{ + /// We're extracting the current symbol even though it + /// doesn't pass all filters because it's a direct dependency + /// of a symbol that does pass all filters and needs + /// information about it (e.g.: base classes outside the filters) + Dependency, + + /// We're extracting the current symbol because it passes + /// all filters + Regular, + + /// We're extracting the current symbol because it passes + /// all filters, but we should also tag it as see-below + /// because it passes one of the see-below filters + SeeBelow, + + /// We're extracting the current symbol because it passes + /// all filters, but we should also tag it as + /// implementation-defined because one of its parents + /// matches the implementation-defined filter + ImplementationDefined, +}; + +/** Return the name of the InfoKind as a string. + */ +constexpr +std::string_view +toString(ExtractionMode kind) noexcept +{ + switch(kind) + { + case ExtractionMode::Dependency: + return "dependency"; + case ExtractionMode::Regular: + return "regular"; + case ExtractionMode::SeeBelow: + return "see-below"; + case ExtractionMode::ImplementationDefined: + return "implementation-defined"; + } + MRDOCS_UNREACHABLE(); +} + +/** Return the InfoKind from a @ref dom::Value string. + */ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + ExtractionMode kind) +{ + v = toString(kind); +} + +/** Compare ExtractionModes and returns the least specific + + This function returns the least specific of the two + ExtractionModes in terms of number of filters passed. + */ +constexpr +ExtractionMode +leastSpecific(ExtractionMode const a, ExtractionMode const b) noexcept +{ + using IT = std::underlying_type_t; + return static_cast( + std::min(static_cast(a), static_cast(b))); +} + +/** Compare ExtractionModes and returns the most specific + + This function returns the most specific of the two + ExtractionModes in terms of number of filters passed. + */ +constexpr +ExtractionMode +mostSpecific(ExtractionMode const a, ExtractionMode const b) noexcept +{ + using IT = std::underlying_type_t; + return static_cast( + std::max(static_cast(a), static_cast(b))); +} + +} // clang::mrdocs + +#endif diff --git a/include/mrdocs/Metadata/Field.hpp b/include/mrdocs/Metadata/Field.hpp index f67a0696fb..c03c019fd0 100644 --- a/include/mrdocs/Metadata/Field.hpp +++ b/include/mrdocs/Metadata/Field.hpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -58,6 +59,8 @@ struct FieldInfo bool HasNoUniqueAddress = false; + std::vector Attributes; + //-------------------------------------------- explicit FieldInfo(SymbolID ID) noexcept diff --git a/include/mrdocs/Metadata/Function.hpp b/include/mrdocs/Metadata/Function.hpp index 326fa34123..9638f03a9a 100644 --- a/include/mrdocs/Metadata/Function.hpp +++ b/include/mrdocs/Metadata/Function.hpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -166,6 +167,8 @@ struct FunctionInfo StorageClassKind StorageClass = StorageClassKind::None; ReferenceKind RefQualifier = ReferenceKind::None; + std::vector Attributes; + //-------------------------------------------- explicit FunctionInfo(SymbolID ID) noexcept diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index 069c133ef0..5e65ebdc60 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -17,15 +17,12 @@ #include #include #include +#include #include -#include #include #include -#include -#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /* Forward declarations */ @@ -74,7 +71,7 @@ struct MRDOCS_VISIBLE /** Kind of declaration. */ - InfoKind Kind; + InfoKind Kind = InfoKind::None; /** Declaration access. @@ -87,18 +84,19 @@ struct MRDOCS_VISIBLE */ AccessKind Access = AccessKind::None; - /** Implicitly extracted flag. + /** Determine why a symbol is extracted. - This flag distinguishes primary `Info` from its dependencies. + This flag distinguishes `Info` from its dependencies and + indicates why it was extracted. - A primary `Info` is one which was extracted during AST traversal - because it satisfied all configured conditions to be extracted. + Non-dependencies can be extracted in normal mode, + see-below mode, or implementation-defined mode. - An `Info` dependency is one which does not meet the configured + A dependency is a symbol which does not meet the configured conditions for extraction, but had to be extracted due to it being used transitively by a primary `Info`. */ - bool Implicit = false; + ExtractionMode Extraction = ExtractionMode::Dependency; /** The parent symbol, if any. @@ -228,7 +226,14 @@ tag_invoke( Info const& I, DomCorpus const* domCorpus); -} // mrdocs -} // clang +/** Compare two Info objects + */ +MRDOCS_DECL +bool +operator<( + Info const& lhs, + Info const& rhs) noexcept; + +} // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Name.hpp b/include/mrdocs/Metadata/Name.hpp index 795e594504..b18f81651d 100644 --- a/include/mrdocs/Metadata/Name.hpp +++ b/include/mrdocs/Metadata/Name.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -17,8 +18,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { enum class NameKind { @@ -30,8 +30,24 @@ MRDOCS_DECL dom::String toString(NameKind kind) noexcept; -/** Represents a (possibly qualified) symbol name. -*/ +inline +void +tag_invoke( + dom::ValueFromTag, + dom::Value& v, + NameKind const kind) +{ + v = toString(kind); +} + +/** Represents a name for a named `TypeInfo` + + When the `TypeInfo` is a named type, this class + represents the name of the type. + + It also includes the symbol ID of the named type, + so that it can be referenced in the documentation. + */ struct NameInfo { /** The kind of name this is. @@ -47,7 +63,19 @@ struct NameInfo std::string Name; /** The parent name info, if any. - */ + + This recursively includes information about + the parent, such as the symbol ID and + potentially template arguments, when + the parent is a SpecializationNameInfo. + + This is particularly useful because the + parent of `id` could be a primary template. + In this case, the Prefix will contain + this primary template information + and the template arguments. + + */ std::unique_ptr Prefix; constexpr bool isIdentifier() const noexcept { return Kind == NameKind::Identifier; } @@ -55,15 +83,12 @@ struct NameInfo constexpr NameInfo() noexcept - : NameInfo(NameKind::Identifier) - { - } + : NameInfo(NameKind::Identifier) {} + explicit constexpr NameInfo(NameKind kind) noexcept - : Kind(kind) - { - } + : Kind(kind) {} virtual ~NameInfo() = default; }; @@ -80,8 +105,7 @@ struct SpecializationNameInfo constexpr SpecializationNameInfo() noexcept : NameInfo(NameKind::Specialization) - { - } + {} }; template< @@ -138,7 +162,6 @@ tag_invoke( } -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Scope.hpp b/include/mrdocs/Metadata/Scope.hpp index 3296559b39..7b6de6acbb 100644 --- a/include/mrdocs/Metadata/Scope.hpp +++ b/include/mrdocs/Metadata/Scope.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -13,12 +14,12 @@ #include #include +#include #include #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { /** Stores the members and lookups for an Info. @@ -82,7 +83,19 @@ generateScopeOverloadsArray( ScopeInfo const& I, DomCorpus const& domCorpus); -} // mrdocs -} // clang +/** Emplace a member Info into a ScopeInfo + + Given a ScopeInfo `P` and an Info `C`, this function will + add the `C.id` to `P.Members` if it's not already there, + and add the `C.id` to `P.Lookups[C.Name]` if it's not + already there. +*/ +MRDOCS_DECL +void +addMember( + ScopeInfo& P, + Info& C); + +} // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Source.hpp b/include/mrdocs/Metadata/Source.hpp index 44748f4a7d..2adeee27f0 100644 --- a/include/mrdocs/Metadata/Source.hpp +++ b/include/mrdocs/Metadata/Source.hpp @@ -24,11 +24,11 @@ namespace mrdocs { enum class FileKind { - // file in the source directory + // File in the source directory Source, - // file in a system include directory + // File in a system include directory System, - // file outside the source directory + // File outside the source directory Other }; diff --git a/include/mrdocs/Metadata/Template.hpp b/include/mrdocs/Metadata/Template.hpp index 343771ffd8..ec802884d5 100644 --- a/include/mrdocs/Metadata/Template.hpp +++ b/include/mrdocs/Metadata/Template.hpp @@ -329,7 +329,7 @@ visit( enum class TemplateSpecKind { - Primary = 0, // for bitstream + Primary, Explicit, Partial }; @@ -342,16 +342,6 @@ toString(TemplateSpecKind kind); */ struct TemplateInfo { - /* For primary templates: - - Params will be non-empty - - Args will be empty - For explicit specializations: - - Params will be empty - For partial specializations: - - Params will be non-empty - - each template parameter will appear at least - once in Args outside of a non-deduced context - */ std::vector> Params; std::vector> Args; @@ -366,9 +356,10 @@ struct TemplateInfo // KRYSTIAN NOTE: using the presence of args/params // to determine the specialization kind *should* work. // emphasis on should. - TemplateSpecKind specializationKind() const noexcept + TemplateSpecKind + specializationKind() const noexcept { - if(Params.empty()) + if (Params.empty()) { return TemplateSpecKind::Explicit; } diff --git a/include/mrdocs/Metadata/Type.hpp b/include/mrdocs/Metadata/Type.hpp index bb3941d6b8..1d043e8c6a 100644 --- a/include/mrdocs/Metadata/Type.hpp +++ b/include/mrdocs/Metadata/Type.hpp @@ -23,8 +23,7 @@ #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { enum QualifierKind { @@ -88,6 +87,15 @@ tag_invoke( v = toString(kind); } +/** A possibly qualified type. + + This class represents a type that may have + qualifiers (e.g. const, volatile). + + This base class is used to store the kind + of type. Derived classes are used to store + the type information according to the kind. + */ struct TypeInfo { /** The kind of TypeInfo this is @@ -158,7 +166,7 @@ tag_invoke( } template -struct IsType : TypeInfo +struct TypeInfoCommonBase : TypeInfo { static constexpr TypeKind kind_id = K; @@ -174,28 +182,28 @@ struct IsType : TypeInfo protected: constexpr - IsType() noexcept + TypeInfoCommonBase() noexcept : TypeInfo(K) { } }; struct NamedTypeInfo - : IsType + : TypeInfoCommonBase { QualifierKind CVQualifiers = QualifierKind::None; std::unique_ptr Name; }; struct DecltypeTypeInfo - : IsType + : TypeInfoCommonBase { QualifierKind CVQualifiers = QualifierKind::None; ExprInfo Operand; }; struct AutoTypeInfo - : IsType + : TypeInfoCommonBase { QualifierKind CVQualifiers = QualifierKind::None; AutoKind Keyword = AutoKind::Auto; @@ -203,7 +211,7 @@ struct AutoTypeInfo }; struct LValueReferenceTypeInfo - : IsType + : TypeInfoCommonBase { std::unique_ptr PointeeType; @@ -214,7 +222,7 @@ struct LValueReferenceTypeInfo }; struct RValueReferenceTypeInfo - : IsType + : TypeInfoCommonBase { std::unique_ptr PointeeType; @@ -225,7 +233,7 @@ struct RValueReferenceTypeInfo }; struct PointerTypeInfo - : IsType + : TypeInfoCommonBase { QualifierKind CVQualifiers = QualifierKind::None; std::unique_ptr PointeeType; @@ -237,7 +245,7 @@ struct PointerTypeInfo }; struct MemberPointerTypeInfo - : IsType + : TypeInfoCommonBase { QualifierKind CVQualifiers = QualifierKind::None; std::unique_ptr ParentType; @@ -250,7 +258,7 @@ struct MemberPointerTypeInfo }; struct ArrayTypeInfo - : IsType + : TypeInfoCommonBase { std::unique_ptr ElementType; ConstantExprInfo Bounds; @@ -262,7 +270,7 @@ struct ArrayTypeInfo }; struct FunctionTypeInfo - : IsType + : TypeInfoCommonBase { std::unique_ptr ReturnType; std::vector> ParamTypes; @@ -340,7 +348,6 @@ toString( std::string_view Name = ""); -} // mrdocs -} // clang +} // clang::mrdocs #endif diff --git a/include/mrdocs/Metadata/Using.hpp b/include/mrdocs/Metadata/Using.hpp index a21d0e88ce..7d29e96124 100644 --- a/include/mrdocs/Metadata/Using.hpp +++ b/include/mrdocs/Metadata/Using.hpp @@ -25,7 +25,7 @@ enum class UsingClass { Normal = 0, // using Typename, // using typename - Enum, // using enum + Enum // using enum }; static constexpr diff --git a/include/mrdocs/Support/Concepts.hpp b/include/mrdocs/Support/Concepts.hpp new file mode 100644 index 0000000000..1495ddace8 --- /dev/null +++ b/include/mrdocs/Support/Concepts.hpp @@ -0,0 +1,24 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_SUPPORT_CONCEPTS_HPP +#define MRDOCS_API_SUPPORT_CONCEPTS_HPP + +#include + +namespace clang::mrdocs { + +template +concept range_of = std::ranges::range && std::same_as, T>; + +} // namespace clang::mrdocs + + +#endif // MRDOCS_API_SUPPORT_CONCEPTS_HPP diff --git a/include/mrdocs/Support/Glob.hpp b/include/mrdocs/Support/Glob.hpp new file mode 100644 index 0000000000..288844c0b1 --- /dev/null +++ b/include/mrdocs/Support/Glob.hpp @@ -0,0 +1,292 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_SUPPORT_GLOBPATTERN_HPP +#define MRDOCS_API_SUPPORT_GLOBPATTERN_HPP + +#include +#include +#include +#include + +namespace clang::mrdocs { + +/** A glob pattern matcher + + @li "*" matches all characters except delimiters. + @li "**" matches all characters + @li "?" matches any single character. + @li "[]" matches one character in the bracket. + @li "[-]" matches one character in the bracket range. + @li "[^]" or "[!]" matches one character not in the bracket. + @li "{,...}" matches one of the globs in the list. + @li "\" escapes the next character so it is treated as a literal. + + Nested brace expansions "{,"{,...}",...}" are not supported. + */ +class GlobPattern { + struct Impl; + std::unique_ptr impl_; +public: + /** Constructs a GlobPattern with the given pattern. + + @param pattern The glob pattern to use for matching. + */ + static + Expected + create(std::string_view pattern, std::optional maxSubGlobs); + + static + Expected + create(std::string_view pattern) + { + return create(pattern, std::nullopt); + } + + /** Destructor */ + ~GlobPattern(); + + /** Construct an empty GlobPattern. + + An empty GlobPattern will never match any string. + */ + GlobPattern(); + + /** Copy constructor */ + GlobPattern(GlobPattern const& other); + + /** Move constructor */ + GlobPattern(GlobPattern&& other) noexcept; + + /** Copy assignment */ + GlobPattern& + operator=(GlobPattern const& other); + + /** Move assignment */ + GlobPattern& + operator=(GlobPattern&& other) noexcept; + + /** Matches the given string against the glob pattern. + + @param str The string to match against the pattern. + @return true if the string matches the pattern, false otherwise. + */ + bool + match(std::string_view str, char delimiter) const; + + /** Matches the start of a given string against the glob pattern. + + This function determines if the given string with + the specified `prefix` can potentially match + the glob pattern. + + If the string matches the start of the pattern without failure, even + if there are characters left in the string or the pattern, the + function returns true. + + @param prefix The string to match against the pattern. + @return true if the string prefix matches the pattern, false otherwise. + */ + bool + matchPatternPrefix(std::string_view prefix, char delimiter) const; + + /** Returns the glob pattern. + + @return The glob pattern as a string view. + */ + std::string_view + pattern() const; +}; + +/** A glob pattern matcher for paths + + A glob pattern matcher where "*" does not match path separators. + The pattern "**" can be used to match any number of path separators. + */ +class PathGlobPattern { + GlobPattern glob_; +public: + /** Constructs a PathGlobPattern with the given pattern. + + @param pattern The glob pattern to use for matching. + @param maxSubGlobs The maximum number of subpatterns allowed. + */ + static + Expected + create( + std::string_view const pattern, + std::optional maxSubGlobs) + { + MRDOCS_TRY(auto glob, GlobPattern::create(pattern, maxSubGlobs)); + return PathGlobPattern{std::move(glob)}; + } + + /** Constructs a PathGlobPattern with the given pattern. + + @param pattern The glob pattern to use for matching. + */ + static + Expected + create(std::string_view const pattern) + { + MRDOCS_TRY(auto glob, GlobPattern::create(pattern)); + return PathGlobPattern{std::move(glob)}; + } + + /** Construct an empty PathGlobPattern. + + An empty PathGlobPattern will never match any string. + */ + PathGlobPattern() = default; + + /** Construct an empty PathGlobPattern. + + An empty PathGlobPattern will never match any string. + */ + explicit + PathGlobPattern(GlobPattern glob) + : glob_{std::move(glob)} + {}; + + /** Matches the given string against the glob pattern. + + @param str The string to match against the pattern. + @return true if the string matches the pattern, false otherwise. + */ + bool + match(std::string_view const str) const + { + return glob_.match(str, '/'); + } + + /** Matches the start of a given string against the glob pattern. + + This function determines if the given string with + the specified `prefix` can potentially match + the glob pattern. + + If the string matches the start of the pattern without failure, even + if there are characters left in the string or the pattern, the + function returns true. + + @param prefix The string to match against the pattern. + @return true if the string prefix matches the pattern, false otherwise. + */ + bool + matchPatternPrefix(std::string_view prefix) const + { + return glob_.matchPatternPrefix(prefix, '/'); + } + + /** Returns the glob pattern. + + @return The glob pattern as a string view. + */ + std::string_view + pattern() const + { + return glob_.pattern(); + } +}; + +/** A glob pattern matcher for C++ symbols + + A glob pattern matcher where "*" does not match "::". + The pattern "**" can be used to match any number of "::". + */ +class SymbolGlobPattern { + GlobPattern glob_; +public: + /** Constructs a SymbolGlobPattern with the given pattern. + + @param pattern The glob pattern to use for matching. + @param maxSubGlobs The maximum number of subpatterns allowed. + */ + static + Expected + create( + std::string_view const pattern, + std::optional maxSubGlobs) + { + MRDOCS_TRY(auto glob, GlobPattern::create(pattern, maxSubGlobs)); + return SymbolGlobPattern{std::move(glob)}; + } + + /** Constructs a SymbolGlobPattern with the given pattern. + + @param pattern The glob pattern to use for matching. + */ + static + Expected + create(std::string_view const pattern) + { + MRDOCS_TRY(auto glob, GlobPattern::create(pattern)); + return SymbolGlobPattern{std::move(glob)}; + } + + /** Construct an empty SymbolGlobPattern. + + An empty SymbolGlobPattern will never match any string. + */ + SymbolGlobPattern() = default; + + /** Construct an empty SymbolGlobPattern. + + An empty SymbolGlobPattern will never match any string. + */ + explicit + SymbolGlobPattern(GlobPattern glob) + : glob_{std::move(glob)} + {}; + + /** Matches the given string against the glob pattern. + + @param str The string to match against the pattern. + @return true if the string matches the pattern, false otherwise. + */ + bool + match(std::string_view const str) const + { + return glob_.match(str, ':'); + } + + /** Matches the start of a given string against the glob pattern. + + This function determines if the given string with + the specified `prefix` can potentially match + the glob pattern. + + If the string matches the start of the pattern without failure, even + if there are characters left in the string or the pattern, the + function returns true. + + @param prefix The string to match against the pattern. + @return true if the string prefix matches the pattern, false otherwise. + */ + bool + matchPatternPrefix(std::string_view prefix) const + { + return glob_.matchPatternPrefix(prefix, ':'); + } + + /** Returns the glob pattern. + + @return The glob pattern as a string view. + */ + std::string_view + pattern() const + { + return glob_.pattern(); + } +}; + +} // clang::mrdocs + +#endif // MRDOCS_API_SUPPORT_GLOBPATTERN_HPP \ No newline at end of file diff --git a/include/mrdocs/Support/ScopeExit.hpp b/include/mrdocs/Support/ScopeExit.hpp new file mode 100644 index 0000000000..8c2f5613ca --- /dev/null +++ b/include/mrdocs/Support/ScopeExit.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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_SUPPORT_SCOPEEXIT_HPP +#define MRDOCS_API_SUPPORT_SCOPEEXIT_HPP + +#include + +namespace clang::mrdocs { + +template +class ScopeExit { + F onExitScope_; + bool dismissed_{false}; +public: + explicit ScopeExit(F onExitScope) + : onExitScope_(std::move(onExitScope)) {} + + ~ScopeExit() { + if (!dismissed_) { + onExitScope_(); + } + } + + void + dismiss() { + dismissed_ = true; + } +}; + +template +class ScopeExitRestore { + T prev_; + T& ref_; + bool dismissed_{false}; +public: + explicit + ScopeExitRestore(T& ref) + : prev_(ref), ref_(ref) + {} + + explicit + ScopeExitRestore(T& ref, T next) + : prev_(ref), ref_(ref) + { + ref_ = next; + } + + ~ScopeExitRestore() + { + if (!dismissed_) { + ref_ = prev_; + } + } + + void + dismiss() { + dismissed_ = true; + } +}; + +template +ScopeExit(F) -> ScopeExit; + +template +ScopeExitRestore(T&) -> ScopeExitRestore; + +template +ScopeExitRestore(T&, T) -> ScopeExitRestore; + +} + +#endif // MRDOCS_API_SUPPORT_SCOPEEXIT_HPP \ No newline at end of file diff --git a/include/mrdocs/Support/String.hpp b/include/mrdocs/Support/String.hpp index b30e81e819..b5a4767752 100644 --- a/include/mrdocs/Support/String.hpp +++ b/include/mrdocs/Support/String.hpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -13,27 +14,17 @@ #include #include +#include #include namespace clang { namespace mrdocs { /** Return the substring without leading horizontal whitespace. -*/ -std::string_view ltrim(std::string_view s) noexcept; - -/** Return the substring without trailing horizontal whitespace. -*/ -std::string_view rtrim(std::string_view s) noexcept; - -/** Return the substring without leading and trailing horizontal whitespace. -*/ -std::string_view trim(std::string_view s) noexcept; - -inline + */ +constexpr std::string_view -ltrim( - std::string_view s) noexcept +ltrim(std::string_view s) noexcept { auto it = s.begin(); for (;; ++it) @@ -46,10 +37,11 @@ ltrim( return s.substr(it - s.begin()); } -inline +/** Return the substring without trailing horizontal whitespace. + */ +constexpr std::string_view -rtrim( - std::string_view s) noexcept +rtrim(std::string_view s) noexcept { auto it = s.end() - 1; while(it > s.begin() && std::isspace(*it)) @@ -57,10 +49,11 @@ rtrim( return s.substr(0, it - s.begin()); } -inline +/** Return the substring without leading and trailing horizontal whitespace. + */ +constexpr std::string_view -trim( - std::string_view s) noexcept +trim(std::string_view s) noexcept { auto left = s.begin(); for (;; ++left) @@ -76,6 +69,13 @@ trim( return std::string_view(&*left, right - left + 1); } +/** Return the substring without leading and trailing horizontal whitespace. +*/ +MRDOCS_DECL +void +replace(std::string& s, std::string_view from, std::string_view to); + + } // mrdocs } // clang diff --git a/include/mrdocs/mrdocs.natvis b/include/mrdocs/mrdocs.natvis index 516bd791c9..08f3e4917e 100644 --- a/include/mrdocs/mrdocs.natvis +++ b/include/mrdocs/mrdocs.natvis @@ -57,8 +57,9 @@ - {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 + empty SymbolID + global 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} @@ -86,7 +87,7 @@ - + {psz_,s} diff --git a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs index e11bc18378..d38cb499fd 100644 --- a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs @@ -53,7 +53,7 @@ [,cols=2] |=== |Name |Description -{{#each symbol.members}} +{{#each (filter_by symbol.members "isRegular" "isSeeBelow")}} {{#if (ne kind "enum-constant")}} |xref:{{{anchor}}}[`{{>symbol/name . nolink=true}}`] {{else}} @@ -145,7 +145,7 @@ {{else}} {{#with (flattenUnique symbol.members "doc.tparams" "name") as |allTParams|}} {{#if (ne (len allTParams) 0)}} -{{#> markup/dynamic-level-h }}Parameters{{/markup/dynamic-level-h}} +{{#> markup/dynamic-level-h }}Template Parameters{{/markup/dynamic-level-h}} |=== | Name | Description @@ -168,7 +168,7 @@ | Name | Description {{#each symbol.doc.params}} -| *{{name}}* +| *{{name}}*{{#if direction}} [{{direction}}]{{/if}} | {{{description}}} {{/each}} |=== @@ -196,7 +196,7 @@ {{#> markup/dynamic-level-h }}Preconditions{{/markup/dynamic-level-h}} {{#each symbol.doc.preconditions}} -{{{.}}} +* {{{.}}} {{/each}} {{/if}} @@ -205,7 +205,7 @@ {{#> markup/dynamic-level-h }}Postconditions{{/markup/dynamic-level-h}} {{#each symbol.doc.postconditions}} -{{{.}}} +* {{{.}}} {{/each}} {{/if}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs b/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs index 442b4abca6..87eb259a8f 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/members-table.hbs @@ -11,7 +11,7 @@ See: https://mrdocs.com/docs/mrdocs/develop/generators.html#dom_reference --}} -{{#if members}} +{{#if (any_of_by members "isRegular" "isSeeBelow")}} {{#>markup/h level=(select @root.config.multipage 1 2)}}{{title}}{{/markup/h}} {{#>markup/table cols=2}} {{#>markup/thead}} @@ -21,7 +21,7 @@ {{/markup/tr}} {{/markup/thead}} {{#>markup/tbody}} -{{#each (sort_by members "name")}} +{{#each (filter_by (sort_by members "name") "isRegular" "isSeeBelow")}} {{> symbol/detail/members-table-row .}} {{/each}} {{/markup/tbody}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/name-info.hbs b/share/mrdocs/addons/generator/common/partials/symbol/name-info.hbs index ff216262e4..781b6a5afe 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/name-info.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/name-info.hbs @@ -1,6 +1,10 @@ {{!-- Renders a Name Info object. + Name Info objects are typically fields of a Type Object. + They carry information about the Symbol the type + refers to. + Expected Context: {Name Info Object} Optional parameters: @@ -11,13 +15,13 @@ See: https://mrdocs.com/docs/mrdocs/develop/generators.html#dom_reference --}} -{{#if prefix~}} - {{> symbol/name-info prefix nolink=nolink~}}:: -{{~/if~}} -{{#if (contains (arr "see-below" "implementation-defined") name)~}} - {{! These are special names that should not be linked. }} - {{ str '_'}}{{name}}{{ str '_'~}} +{{#if symbol.isImplementationDefined~}} + {{! These are special names that should not be linked. ~}} + {{ str '/* '}}implementation-defined{{ str ' */'~}} {{else~}} + {{#if prefix~}} + {{> symbol/name-info prefix nolink=nolink~}}:: + {{~/if~}} {{! Render the name of the symbol. ~}} {{#if (and symbol.url (not nolink))~}} {{! Link to the symbol's documentation. ~}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/name.hbs b/share/mrdocs/addons/generator/common/partials/symbol/name.hbs index d2b7085cfc..afa0750b88 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/name.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/name.hbs @@ -24,10 +24,10 @@ {{else~}} {{#if (and link.url (not nolink))~}} {{! Symbol with URL: link to the symbol documentation ~}} - {{#>markup/a href=link.url}}{{name}}{{/markup/a~}} + {{#>markup/a href=link.url}}{{or name ""}}{{/markup/a~}} {{else~}} {{! Symbol without URL: plain text ~}} - {{name~}} + {{or name ""~}} {{/if~}} {{#if (contains (arr "explicit" "partial") template.kind)~}} {{! Explicit or partial template: render the template arguments ~}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs b/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs index ffbeb54c0e..7e8499aa9b 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs @@ -42,6 +42,6 @@ {{>symbol/qualified-name symbol nolink=nolink ~}} {{~else~}} {{! Friend type: use the type name ~}} - {{~type.name~}} + {{~>type/declarator type ~}} {{~/if~}} {{~/if~}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/symbol/signature/field.hbs b/share/mrdocs/addons/generator/common/partials/symbol/signature/field.hbs index 2eaa14db0c..cd4c317b88 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/signature/field.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/signature/field.hbs @@ -1,3 +1,5 @@ +{{#if attributes}}[{{join ", " attributes}}] +{{/if}} {{#if isMutable}}mutable {{/if~}} {{>type/declarator-prefix type}} {{>symbol/name symbol~}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/signature/function.hbs b/share/mrdocs/addons/generator/common/partials/symbol/signature/function.hbs index 987e562935..ceb4a159f3 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/signature/function.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/signature/function.hbs @@ -2,6 +2,8 @@ {{/if~}} {{#if isFriend}}friend {{/if~}} +{{#if attributes}}{{#unless isFriend}}[{{join ", " attributes}}] +{{/unless}}{{/if~}} {{#if constexprKind}}{{constexprKind}} {{/if~}} {{#if storageClass}}{{storageClass}} @@ -23,7 +25,9 @@ {{#if refQualifier}} {{refQualifier}}{{/if~}} {{#if exceptionSpec}} {{exceptionSpec}}{{/if~}} {{#if (eq class "normal")}}{{>type/declarator-suffix return}}{{/if~}} -{{#if requires}} requires {{requires}}{{/if~}} +{{#if requires}} + +requires {{requires}}{{/if~}} {{#if hasOverrideAttr}} override{{/if~}} {{#if isFinal}} final{{/if~}} {{#if isPure}} = 0{{/if~}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/signature/record.hbs b/share/mrdocs/addons/generator/common/partials/symbol/signature/record.hbs index ac15fd774e..0afb25ed27 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/signature/record.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/signature/record.hbs @@ -12,4 +12,4 @@ {{#unless (eq access ../defaultAccess)}} {{access}}{{/unless~}} {{#if isVirtual}} virtual{{/if}} {{>type/declarator type~}} {{/each~}} -{{/unless}}; \ No newline at end of file +{{/unless}}{{#isSeeBelow}} { /* see-below */ }{{/isSeeBelow}}; \ No newline at end of file diff --git a/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs b/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs index 7ad8a07752..cb63c3a183 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/tranche.hbs @@ -15,6 +15,7 @@ See: https://mrdocs.com/docs/mrdocs/develop/generators.html#dom_reference --}} {{>symbol/members-table members=tranche.namespaces title="Namespaces"}} +{{>symbol/members-table members=tranche.namespace-aliases title="Namespace Aliases"}} {{>symbol/members-table members=(concat tranche.records tranche.types) title=(concat (select label (concat label " ") "") "Types")}} {{>symbol/members-table members=tranche.enums title=(concat (select label (concat label " ") "") "Enums")}} {{#if is-namespace}} diff --git a/share/mrdocs/addons/generator/common/partials/template/head.hbs b/share/mrdocs/addons/generator/common/partials/template/head.hbs index 0152caeb67..f6dff735e3 100644 --- a/share/mrdocs/addons/generator/common/partials/template/head.hbs +++ b/share/mrdocs/addons/generator/common/partials/template/head.hbs @@ -13,4 +13,5 @@ template{{ str '<' }}{{#each params}}{{#unless (and @first @last)}} {{/unless}}{{>template/param~}} {{#unless @last}},{{/unless~}} -{{/each}}{{ str '>' }}{{#if requires}} requires {{requires}}{{/if}} \ No newline at end of file +{{/each}}{{ str '>' }}{{#if requires}} +requires {{requires}}{{/if}} \ No newline at end of file diff --git a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs index 706b1c67d3..13024a958d 100644 --- a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs @@ -65,7 +65,7 @@ -{{#each symbol.members}} +{{#each (filter_by symbol.members "isRegular" "isSeeBelow")}} {{#if (ne kind "enum-constant")}} {{>symbol/name . nolink=true}} @@ -235,7 +235,7 @@ {{#each symbol.doc.params}} -{{name}} +{{name}}{{#if direction}} [{{direction}}]{{/if}} {{{description}}} {{/each}} @@ -272,18 +272,22 @@ {{#if symbol.doc.preconditions}}
{{#> markup/dynamic-level-h level=2 }}Preconditions{{/markup/dynamic-level-h}} +
    {{#each symbol.doc.preconditions}} -

    {{{.}}}

    +
  • {{{.}}}
  • {{/each}} +
{{/if}} {{! Postconditions }} {{#if symbol.doc.postconditions}}
{{#> markup/dynamic-level-h level=2 }}Postconditions{{/markup/dynamic-level-h}} +
    {{#each symbol.doc.postconditions}} -

    {{{.}}}

    +
  • {{{.}}}
  • {{/each}} +
{{/if}} {{! See Also }} diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 8ae3e7a3ec..c5442f0758 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -19,9 +20,8 @@ #include "lib/Support/Debug.hpp" #include "lib/Support/Glob.hpp" #include "lib/Lib/Diagnostics.hpp" -#include "lib/Lib/Filters.hpp" -#include "lib/AST/SymbolFilterScope.hpp" #include +#include #include #include #include @@ -45,7 +45,7 @@ namespace clang::mrdocs { auto ASTVisitor::FileInfo::build( - std::span const> search_dirs, + std::span const> /* search_dirs */, std::string_view const file_path, std::string_view const sourceRoot) -> ASTVisitor::FileInfo { FileInfo file_info; @@ -128,7 +128,6 @@ ASTVisitor( , context_(context) , source_(context.getSourceManager()) , sema_(sema) - , symbolFilter_(config->symbolFilter) { // Install handlers for our custom commands initCustomCommentCommands(context_); @@ -211,283 +210,219 @@ build() // traverse the translation unit, only extracting // declarations which satisfy all filter conditions. // dependencies will be tracked, but not extracted - traverseAny(context_.getTranslationUnitDecl()); + auto* TU = context_.getTranslationUnitDecl(); + traverse(TU); MRDOCS_ASSERT(find(SymbolID::global)); - - // Dependency extraction is disabled: we are done - if(config_->referencedDeclarations == - ConfigImpl::SettingsImpl::ExtractPolicy::Never) { - return; - } - buildDependencies(); } -void +template < + class InfoTy, + std::derived_from DeclTy> +Info* ASTVisitor:: -buildDependencies() +traverse(DeclTy* D) { - auto scope = enterMode(ExtractMode::DirectDependency); - std::unordered_set currentPass(std::move(dependencies_)); - while (!currentPass.empty()) + MRDOCS_ASSERT(D); + MRDOCS_CHECK_OR(!D->isInvalidDecl(), nullptr); + MRDOCS_SYMBOL_TRACE(D, context_); + + if constexpr (std::same_as) { - for (Decl* D : currentPass) + // Convert to the most derived type of the Decl + // and call the appropriate traverse function + return visit(D, [&](DeclTyU* U) -> Info* { - SymbolID id = generateID(D); - bool const invalidId = !id; - bool const alreadyExtracted = info_.contains(id); - if (invalidId || alreadyExtracted) + if constexpr (!std::same_as) { - continue; + return traverse(U); } - traverseAny(D); - } - currentPass = std::move(dependencies_); + return nullptr; + }); } -} + else if constexpr (HasInfoTypeFor || std::derived_from) + { + // If the declaration has a corresponding Info type, + // we build the Info object and populate it with the + // necessary information. + using R = std::conditional_t< + std::same_as, + InfoTypeFor_t, + InfoTy>; -namespace { -template -concept isRedeclarableTemplate = - std::derived_from; + auto exp = upsert(D); + MRDOCS_CHECK_OR(exp, nullptr); + auto& [I, isNew] = *exp; -template -concept isTemplateSpecialization = - std::derived_from || - std::derived_from; -} // (anon) + // Populate the base classes with the necessary information. + // Even when the object is new, we want to update the source locations + // and the documentation status. + populateInfoBases(I, isNew, D); + // Populate the derived Info object with the necessary information + // when the object is new. If the object already exists, this + // information would be redundant. + populate(I, D); -void -ASTVisitor:: -traverseAny(Decl* D) -{ - MRDOCS_ASSERT(D); - MRDOCS_CHECK_OR(!D->isInvalidDecl()); + // Traverse the members of the declaration according to the + // current extraction mode. + traverseMembers(I, D); - // Restore the symbol filter state when leaving the scope - SymbolFilterScope scope(symbolFilter_); + // Traverse the parents of the declaration in dependency mode. + traverseParents(I, D); - // Convert to the most derived type of the Decl - // and call the appropriate traverse function - visit(D, [&](DeclTy* DD) + return &I; + } + return nullptr; +} + +Info* +ASTVisitor:: +traverse(FunctionTemplateDecl* D) +{ + // Route the traversal to GuideInfo or FunctionInfo + if (FunctionDecl* FD = D->getTemplatedDecl(); + FD && isa(FD)) { - if constexpr (isRedeclarableTemplate) - { - // If the symbol is a template, we traverse the - // templated declaration instead of the template. - // The template is provided as the second argument - // so that the Info object can also be populated with - // this information. - traverseAny(DD->getTemplatedDecl(), DD); - } - else if constexpr (isTemplateSpecialization) - { - // If the symbol is a template specialization, - // we traverse the specialized template and provide - // the specialized template as the second argument. - traverse(DD, DD->getSpecializedTemplate()); - } - else - { - // In the general case, we call the appropriate - // traverse function for the most derived type - // of the Decl - traverse(DD); - } - }); + return traverse(D); + } + return traverse(D); } -template TemplateDeclTy> -void +Info* ASTVisitor:: -traverseAny(Decl* D, TemplateDeclTy* TD) +traverse(UsingDirectiveDecl* D) { - MRDOCS_ASSERT(D); - MRDOCS_ASSERT(TD); - MRDOCS_CHECK_OR(!D->isInvalidDecl()); - MRDOCS_CHECK_OR(!TD->isInvalidDecl()); + // Find the parent namespace + ScopeExitRestore s1(mode_, ExtractionMode::Dependency); + Decl* P = getParent(D); + MRDOCS_SYMBOL_TRACE(P, context_); + Info* PI = findOrTraverse(P); + MRDOCS_CHECK_OR(PI, nullptr); + auto PNI = dynamic_cast(PI); + MRDOCS_CHECK_OR(PNI, nullptr); - // Restore the symbol filter state when leaving the scope - SymbolFilterScope scope(symbolFilter_); + // Find the nominated namespace + Decl* ND = D->getNominatedNamespace(); + MRDOCS_SYMBOL_TRACE(ND, context_); + ScopeExitRestore s2(mode_, ExtractionMode::Dependency); + Info* NDI = findOrTraverse(ND); + MRDOCS_CHECK_OR(NDI, nullptr); - // Convert to the most derived type of the Decl - // and call the appropriate traverse function - visit(D, [&](DeclTy* DD) + if (std::ranges::find(PNI->UsingDirectives, NDI->id) == PNI->UsingDirectives.end()) { - if constexpr ( - isRedeclarableTemplate || - isTemplateSpecialization) - { - // If `D` is still a template or a template specialization, - // we traverse the secondary templated declaration instead. - traverseAny(DD); - } - else - { - // Call the appropriate traverse function - // for the most derived type of the Decl. - // The primary template information provided to - // the corresponding traverse function. - traverse(DD, TD); - } - }); + PNI->UsingDirectives.emplace_back(NDI->id); + } + return nullptr; +} + +Info* +ASTVisitor:: +traverse(IndirectFieldDecl* D) +{ + return traverse(D->getAnonField()); } template < - bool PopulateFromTD, - std::derived_from DeclTy, - std::derived_from TemplateDeclTy> + std::derived_from InfoTy, + std::derived_from DeclTy> +requires (!std::derived_from) void ASTVisitor:: -defaultTraverseImpl(DeclTy* D, TemplateDeclTy* TD) +traverseMembers(InfoTy& I, DeclTy* DC) { - MRDOCS_ASSERT(D); - MRDOCS_ASSERT(!PopulateFromTD || TD); - - // If D is also a template declaration, we extract - // the template information from the declaration itself - if constexpr (!HasInfoTypeFor) - { - // Traverse the members of the declaration even if - // the declaration does not have a corresponding Info type - traverseMembers(D); - } - else + // When a declaration context is a function, we should + // not traverse its members as function arguments are + // not main Info members. + if constexpr ( + !std::derived_from && + std::derived_from) { - if constexpr (!PopulateFromTD && std::derived_from) - { - defaultTraverseImpl(D, D); - return; - } - - // If the declaration has a corresponding Info type, - // we build the Info object and populate it with the - // necessary information. - auto exp = upsert(D); - MRDOCS_CHECK_OR(exp); + // We only need members of regular symbols and see-below namespaces + // - SeeBelow symbols (that are not namespaces) are only the + // symbol and the documentation. + // - ImplementationDefined symbols have no pages + // - Dependency symbols only need the names + MRDOCS_CHECK_OR( + I.Extraction == ExtractionMode::Regular || + I.Extraction == ExtractionMode::SeeBelow); + MRDOCS_CHECK_OR( + I.Extraction != ExtractionMode::SeeBelow || + I.Kind == InfoKind::Namespace); - auto& [I, isNew] = *exp; + // Set the context for the members + ScopeExitRestore s(mode_, I.Extraction); - // Populate template information - if constexpr ( - PopulateFromTD && - requires { I.Template; }) - { - if (!I.Template) + // There are many implicit declarations in the + // translation unit declaration, so we preemtively + // skip them here. + auto explicitMembers = std::ranges::views::filter(DC->decls(), [](Decl* D) { - I.Template.emplace(); - } - populate(*I.Template, D, TD); + return !D->isImplicit() || isa(D); + }); + for (auto* D : explicitMembers) + { + traverse(D); } - - // Populate the Info object with the necessary information - populate(I, isNew, D); - - // Traverse the members of the declaration - traverseMembers(D); } } -template -requires - std::derived_from && - std::derived_from +template < + std::derived_from InfoTy, + std::derived_from DeclTy> void ASTVisitor:: -traverse(FunctionDeclTy* D) +traverseMembers(InfoTy& I, DeclTy* D) { - auto exp = upsert(D); - if (!exp) - { - return; - } - auto [I, isNew] = *exp; + traverseMembers(I, D->getTemplatedDecl()); +} - // D is the templated declaration if FTD is non-null - if (D->isFunctionTemplateSpecialization()) +template < + std::derived_from InfoTy, + std::derived_from DeclTy> +requires (!std::derived_from) +void +ASTVisitor:: +traverseParents(InfoTy& I, DeclTy* DC) +{ + MRDOCS_SYMBOL_TRACE(DC, context_); + if (Decl* PD = getParent(DC)) { - if (!I.Template) - { - I.Template.emplace(); - } + MRDOCS_SYMBOL_TRACE(PD, context_); - if (auto* FTSI = D->getTemplateSpecializationInfo()) - { - generateID(getInstantiatedFrom( - FTSI->getTemplate()), I.Template->Primary); + // Check if we haven't already extracted or started + // to extract the parent scope - // TemplateArguments is used instead of TemplateArgumentsAsWritten - // because explicit specializations of function templates may have - // template arguments deduced from their return type and parameters - if(auto* Args = FTSI->TemplateArguments) + + // Traverse the parent scope as a dependency if it + // hasn't been extracted yet + Info* PI = nullptr; + { + ScopeExitRestore s(mode_, ExtractionMode::Dependency); + if (PI = findOrTraverse(PD); !PI) { - populate(I.Template->Args, Args->asArray()); + return; } } - else if (auto* DFTSI = D->getDependentSpecializationInfo()) + + // If we found the parent scope, set it as the parent + I.Parent = PI->id; + if (auto* SI = dynamic_cast(PI)) { - // Only extract the ID of the primary template if there is - // a single candidate primary template. - if (auto Candidates = DFTSI->getCandidates(); Candidates.size() == 1) - { - generateID(getInstantiatedFrom( - Candidates.front()), I.Template->Primary); - } - if(auto* Args = DFTSI->TemplateArgumentsAsWritten) - { - populate(I.Template->Args, Args); - } + addMember(*SI, I); } } - - populate(I, isNew, D); } +template < + std::derived_from InfoTy, + std::derived_from DeclTy> void ASTVisitor:: -traverse(UsingDirectiveDecl* D) +traverseParents(InfoTy& I, DeclTy* D) { - MRDOCS_CHECK_OR(shouldExtract(D)); - - Decl* PD = getParent(D); - bool const isNamespaceScope = cast(PD)->isFileContext(); - MRDOCS_CHECK_OR(isNamespaceScope); - - if (Info* PI = find(generateID(PD))) - { - MRDOCS_ASSERT(PI->isNamespace()); - auto* NI = dynamic_cast(PI); - upsertDependency( - D->getNominatedNamespaceAsWritten(), - NI->UsingDirectives.emplace_back()); - } + traverseParents(I, D->getTemplatedDecl()); } -void -ASTVisitor:: -traverse(IndirectFieldDecl* D) -{ - traverse(D->getAnonField()); -} - -template DeclTy> -void -ASTVisitor:: -traverseMembers(DeclTy* DC) -{ - // When a declaration context is a function, we should - // not traverse its members as function arguments are - // not main Info members. - if constexpr ( - !std::derived_from && - std::derived_from) - { - for (auto* D : DC->decls()) - { - traverseAny(D); - } - } -} Expected> ASTVisitor:: @@ -522,6 +457,17 @@ generateUSR(const Decl* D) const return res; } + if (auto const* UD = dyn_cast(D)) + { + if (index::generateUSRForDecl(UD->getNominatedNamespace(), res)) + { + return Unexpected(Error("Failed to generate USR")); + } + res.append("@UDDec"); + res.append(UD->getQualifiedNameAsString()); + return res; + } + // Handling UnresolvedUsingTypenameDecl if (auto const* UD = dyn_cast(D)) { @@ -689,36 +635,244 @@ generateID(const Decl* D) const return id; } +template InfoTy, class DeclTy> +void +ASTVisitor:: +populateInfoBases(InfoTy& I, bool const isNew, DeclTy* D) +{ + // Populate the documentation + bool const isDocumented = generateJavadoc(I.javadoc, D); + + // Populate the source info + if constexpr (std::derived_from) + { + bool const isDefinition = [&]() { + if constexpr (requires {D->isThisDeclarationADefinition();}) + { + return D->isThisDeclarationADefinition(); + } + else + { + return false; + } + }(); + clang::SourceLocation Loc = D->getBeginLoc(); + if (Loc.isInvalid()) + { + Loc = D->getLocation(); + } + if (Loc.isValid()) + { + populate(I, Loc, isDefinition, isDocumented); + } + } + + // If a symbol is extracted as a new dependency, check if + // the symbol passes the include filters and already promote + // it to regular. + if (isNew && + I.Extraction == ExtractionMode::Dependency) + { + // Try an exact match here + auto qualifiedName = this->qualifiedName(D); + if (checkSymbolFiltersImpl(std::string_view(qualifiedName.str()))) + { + I.Extraction = ExtractionMode::Regular; + // default mode also becomes regular for its + // members + mode_ = ExtractionMode::Regular; + } + } + + // Determine the reason why the symbol is being extracted + if (I.Extraction == ExtractionMode::Regular || + I.Extraction == ExtractionMode::SeeBelow) + { + auto matchQualifiedName = + [qualifiedName = this->qualifiedName(D)] + (SymbolGlobPattern const& pattern) + { + std::string_view const qualifiedNameSV = qualifiedName.str(); + return pattern.match(std::string_view(qualifiedNameSV)); + }; + + // Promote the extraction mode to SeeBelow if the symbol + // matches any of the patterns or if any of its parents + // are being extracted in SeeBelow mode + if (I.Extraction == ExtractionMode::Regular && + !config_->seeBelow.empty()) + { + if (std::ranges::any_of(config_->seeBelow, matchQualifiedName)) + { + I.Extraction = ExtractionMode::SeeBelow; + } + else if (I.Parent) + { + Info const* PI = find(I.Parent); + while (PI) + { + if (PI->Extraction == ExtractionMode::SeeBelow) + { + I.Extraction = ExtractionMode::SeeBelow; + break; + } + PI = find(PI->Parent); + } + } + else + { + auto* PD = getParent(D); + Info const* PI = findOrTraverse(PD); + while (PI) + { + if (PI->Extraction == ExtractionMode::SeeBelow) + { + I.Extraction = ExtractionMode::SeeBelow; + break; + } + PD = getParent(PD); + PI = find(PD); + } + } + } + + // Promote the extraction mode to ImplementationDefined if the symbol + // matches any of the patterns or if any of its parents + // are being extracted in ImplementationDefined mode + if (!config_->implementationDefined.empty()) + { + if (std::ranges::any_of(config_->implementationDefined, matchQualifiedName)) + { + I.Extraction = ExtractionMode::ImplementationDefined; + } + else if (I.Parent) + { + Info const* PI = find(I.Parent); + while (PI) + { + if (PI->Extraction == ExtractionMode::ImplementationDefined) + { + I.Extraction = ExtractionMode::ImplementationDefined; + break; + } + PI = find(PI->Parent); + } + } + else + { + auto* PD = getParent(D); + Info const* PI = findOrTraverse(PD); + while (PI) + { + if (PI->Extraction == ExtractionMode::ImplementationDefined) + { + I.Extraction = ExtractionMode::ImplementationDefined; + break; + } + PD = getParent(PD); + PI = find(PD); + } + } + } + } + + // All other information is redundant if the symbol is not new + MRDOCS_CHECK_OR(isNew); + + // These should already have been populated by traverseImpl + MRDOCS_ASSERT(I.id); + MRDOCS_ASSERT(I.Kind != InfoKind::None); + + if constexpr (std::same_as) + { + I.Name = extractName(D->getDeducedTemplate()); + } + else if constexpr (std::derived_from) + { + if (auto* FD = D->getFriendDecl()) + { + I.Name = extractName(D->getFriendDecl()); + } + else if (TypeSourceInfo const* FT = D->getFriendType()) + { + llvm::raw_string_ostream os(I.Name); + FT->getType().print(os, context_.getPrintingPolicy()); + } + } + else if constexpr (std::derived_from) + { + I.Name = extractName(D->getNominatedNamespace()); + } + else if constexpr (std::derived_from) + { + I.Name = extractName(D); + } +} + +void +ASTVisitor:: +populate( + SourceInfo& I, + clang::SourceLocation const loc, + bool const definition, + bool const documented) +{ + unsigned line = source_.getPresumedLoc( + loc, false).getLine(); + FileInfo* file = findFileInfo(loc); + MRDOCS_ASSERT(file); + + if (definition) + { + if (I.DefLoc) + { + return; + } + I.DefLoc.emplace(file->full_path, + file->short_path, line, file->kind, + documented); + } + else + { + auto const existing = std::ranges:: + find_if(I.Loc, + [line, file](const Location& l) + { + return l.LineNumber == line && + l.Path == file->full_path; + }); + if (existing != I.Loc.end()) + { + return; + } + I.Loc.emplace_back(file->full_path, + file->short_path, line, file->kind, + documented); + } +} + void ASTVisitor:: populate( NamespaceInfo& I, - bool const isNew, NamespaceDecl* D) { - // Only extract Namespace data once - MRDOCS_CHECK_OR(isNew); I.IsAnonymous = D->isAnonymousNamespace(); if (!I.IsAnonymous) { I.Name = extractName(D); } I.IsInline = D->isInline(); - linkParent(I, D); } void ASTVisitor:: populate( NamespaceInfo& I, - bool const isNew, TranslationUnitDecl*) { - // Only extract Namespace data once - MRDOCS_CHECK_OR(isNew); I.id = SymbolID::global; I.IsAnonymous = false; - I.Name.clear(); I.IsInline = false; } @@ -726,28 +880,13 @@ void ASTVisitor:: populate( RecordInfo& I, - bool const isNew, CXXRecordDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), - D->isThisDeclarationADefinition(), documented); - - if (!isNew) - { - return; - } - - NamedDecl const* ND = D; - if (TypedefNameDecl const* TD = D->getTypedefNameForAnonDecl()) + if (D->getTypedefNameForAnonDecl()) { I.IsTypeDef = true; - ND = TD; } - I.Name = extractName(ND); - I.KeyKind = toRecordKeyKind(D->getTagKind()); - // These are from CXXRecordDecl::isEffectivelyFinal() I.IsFinal = D->hasAttr(); if (auto const* DT = D->getDestructor()) @@ -759,37 +898,48 @@ populate( // from whichever declaration is the definition (if any) if(D->hasDefinition()) { - for(const CXXBaseSpecifier& B : D->bases()) + for (const CXXBaseSpecifier& B : D->bases()) { AccessSpecifier const access = B.getAccessSpecifier(); - // KRYSTIAN FIXME: we need finer-grained control - // for protected bases, since an inheriting class - // will have access to the bases public members... - if(config_->inaccessibleBases != - ConfigImpl::SettingsImpl::ExtractPolicy::Always) + + if (!config_->privateBases && + access == AccessSpecifier::AS_private) { - if(access == AccessSpecifier::AS_private || - access == AccessSpecifier::AS_protected) - continue; + continue; } - // the extraction of the base type is - // performed in direct dependency mode - auto BaseType = toTypeInfo( - B.getType(), - ExtractMode::DirectDependency); + + QualType const BT = B.getType(); + auto BaseType = toTypeInfo(BT); + // CXXBaseSpecifier::getEllipsisLoc indicates whether the // base was a pack expansion; a PackExpansionType is not built // for base-specifiers - if(BaseType && B.getEllipsisLoc().isValid()) + if (BaseType && B.getEllipsisLoc().isValid()) + { BaseType->IsPackExpansion = true; + } I.Bases.emplace_back( std::move(BaseType), - convertToAccessKind(access), + toAccessKind(access), B.isVirtual()); } } +} + +void +ASTVisitor:: +populate(RecordInfo& I, ClassTemplateDecl* D) +{ + populate(I.Template, D->getTemplatedDecl(), D); + populate(I, D->getTemplatedDecl()); +} - linkParent(I, D); +void +ASTVisitor:: +populate(RecordInfo& I, ClassTemplateSpecializationDecl* D) +{ + populate(I.Template, D, D->getSpecializedTemplate()); + populate(I, cast(D)); } template DeclTy> @@ -797,12 +947,44 @@ void ASTVisitor:: populate( FunctionInfo& I, - bool const isNew, DeclTy* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), - D->isThisDeclarationADefinition(), documented); + // D is the templated declaration if FTD is non-null + if (D->isFunctionTemplateSpecialization()) + { + if (!I.Template) + { + I.Template.emplace(); + } + + if (auto* FTSI = D->getTemplateSpecializationInfo()) + { + generateID(getInstantiatedFrom( + FTSI->getTemplate()), I.Template->Primary); + + // TemplateArguments is used instead of TemplateArgumentsAsWritten + // because explicit specializations of function templates may have + // template arguments deduced from their return type and parameters + if(auto* Args = FTSI->TemplateArguments) + { + populate(I.Template->Args, Args->asArray()); + } + } + else if (auto* DFTSI = D->getDependentSpecializationInfo()) + { + // Only extract the ID of the primary template if there is + // a single candidate primary template. + if (auto Candidates = DFTSI->getCandidates(); Candidates.size() == 1) + { + generateID(getInstantiatedFrom( + Candidates.front()), I.Template->Primary); + } + if(auto* Args = DFTSI->TemplateArgumentsAsWritten) + { + populate(I.Template->Args, Args); + } + } + } // KRYSTIAN TODO: move other extraction that requires // a valid function type here @@ -816,7 +998,7 @@ populate( // // FunctionDecl // - I.OverloadedOperator = convertToOperatorKind( + I.OverloadedOperator = toOperatorKind( D->getOverloadedOperator()); I.IsVariadic |= D->isVariadic(); I.IsDefaulted |= D->isDefaulted(); @@ -853,7 +1035,7 @@ populate( I.IsPure |= D->isPureVirtual(); I.IsConst |= D->isConst(); I.IsVolatile |= D->isVolatile(); - I.RefQualifier = convertToReferenceKind(D->getRefQualifier()); + I.RefQualifier = toReferenceKind(D->getRefQualifier()); I.IsFinal |= D->template hasAttr(); //D->isCopyAssignmentOperator() //D->isMoveAssignmentOperator() @@ -884,11 +1066,12 @@ populate( populate(I.Explicit, D->getExplicitSpecifier()); } - for (ParmVarDecl const* P : D->parameters()) + ArrayRef params = D->parameters(); + I.Params.resize(params.size()); + for (std::size_t i = 0; i < params.size(); ++i) { - Param& param = isNew ? - I.Params.emplace_back() : - I.Params[P->getFunctionScopeIndex()]; + ParmVarDecl const* P = params[i]; + Param& param = I.Params[i]; if (param.Name.empty()) { @@ -909,84 +1092,65 @@ populate( } } - if (!isNew) - { - return; - } - - I.Name = extractName(D); - I.Class = toFunctionClass(D->getDeclKind()); QualType const RT = D->getReturnType(); - auto next_mode = ExtractMode::IndirectDependency; - if (auto const* AT = RT->getContainedAutoType(); - AT && AT->hasUnnamedOrLocalType()) - { - next_mode = ExtractMode::DirectDependency; - } + // extract the return type in direct dependency mode // if it contains a placeholder type which is // deduceded as a local class type - I.ReturnType = toTypeInfo(RT, next_mode); + I.ReturnType = toTypeInfo(RT); - if(auto* TRC = D->getTrailingRequiresClause()) + if (auto* TRC = D->getTrailingRequiresClause()) + { populate(I.Requires, TRC); + } + + if (D->hasAttrs()) + { + for (AttrVec& attrs = D->getAttrs(); + Attr const* attr: attrs) + { + if (IdentifierInfo const* II = attr->getAttrName()) + { + I.Attributes.emplace_back(II->getName()); + } + } + } +} - linkParent(I, D); +void +ASTVisitor:: +populate(FunctionInfo& I, FunctionTemplateDecl* D) +{ + populate(I.Template, D->getTemplatedDecl(), D); + populate(I, D->getTemplatedDecl()); } void ASTVisitor:: populate( EnumInfo& I, - bool const isNew, EnumDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), - D->isThisDeclarationADefinition(), documented); - - if (!isNew) - { - return; - } - - I.Name = extractName(D); - I.Scoped = D->isScoped(); - if (D->isFixed()) { I.UnderlyingType = toTypeInfo(D->getIntegerType()); } - - linkParent(I, D); } void ASTVisitor:: populate( EnumConstantInfo& I, - bool const isNew, EnumConstantDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - I.Name = extractName(D); - populate( I.Initializer, D->getInitExpr(), D->getInitVal()); - - linkParent(I, D); } template TypedefNameDeclTy> @@ -994,58 +1158,37 @@ void ASTVisitor:: populate( TypedefInfo& I, - bool const isNew, TypedefNameDeclTy* D) { - bool const documented = generateJavadoc(I.javadoc, D); - - // KRYSTIAN FIXME: we currently treat typedef/alias - // declarations as having a single definition; however, - // such declarations are never definitions and can - // be redeclared multiple times (even in the same scope) - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - I.IsUsing = isa(D); - I.Name = extractName(D); - - // When a symbol has a dependency on a typedef, we also - // consider the symbol to have a dependency on the aliased - // type. Therefore, we propagate the current dependency mode - // when building the TypeInfo for the aliased type - I.Type = toTypeInfo( - D->getUnderlyingType(), - currentMode()); + QualType const QT = D->getUnderlyingType(); + I.Type = toTypeInfo(QT); +} - linkParent(I, D); +void +ASTVisitor:: +populate(TypedefInfo& I, TypeAliasTemplateDecl* D) +{ + populate(I.Template, D->getTemplatedDecl(), D); + populate(I, D->getTemplatedDecl()); } + void ASTVisitor:: populate( VariableInfo& I, - bool const isNew, VarDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), - D->isThisDeclarationADefinition(), documented); - // KRYSTIAN FIXME: we need to properly merge storage class if (StorageClass const SC = D->getStorageClass()) { I.StorageClass = toStorageClassKind(SC); } - // this handles thread_local, as well as the C // __thread and __Thread_local specifiers I.IsThreadLocal |= D->getTSCSpec() != ThreadStorageClassSpecifier::TSCS_unspecified; - // KRYSTIAN NOTE: VarDecl does not provide getConstexprKind, // nor does it use getConstexprKind to store whether // a variable is constexpr/constinit. Although @@ -1057,104 +1200,77 @@ populate( { I.Constexpr = ConstexprKind::Constexpr; } - if (Expr const* E = D->getInit()) { populate(I.Initializer, E); } - - if (!isNew) - { - return; - } - - I.Name = extractName(D); - I.Type = toTypeInfo(D->getType()); - - linkParent(I, D); } void ASTVisitor:: populate( FieldInfo& I, - bool const isNew, FieldDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - // fields (i.e. non-static data members) - // cannot have multiple declarations - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - - I.Name = extractName(D); - I.Type = toTypeInfo(D->getType()); - I.IsVariant = D->getParent()->isUnion(); - I.IsMutable = D->isMutable(); - - if(const Expr* E = D->getInClassInitializer()) + if (Expr const* E = D->getInClassInitializer()) + { populate(I.Default, E); - + } if(D->isBitField()) { I.IsBitfield = true; - populate( - I.BitfieldWidth, - D->getBitWidth()); + populate(I.BitfieldWidth, D->getBitWidth()); } - I.HasNoUniqueAddress = D->hasAttr(); I.IsDeprecated = D->hasAttr(); I.IsMaybeUnused = D->hasAttr(); + if (D->hasAttrs()) + { + for (AttrVec& attrs = D->getAttrs(); + Attr const* attr: attrs) + { + I.Attributes.emplace_back(attr->getAttrName()->getName()); + } + } +} + +void +ASTVisitor:: +populate(VariableInfo& I, VarTemplateDecl* D) +{ + populate(I.Template, D->getTemplatedDecl(), D); + populate(I, D->getTemplatedDecl()); +} - linkParent(I, D); +void +ASTVisitor:: +populate(VariableInfo& I, VarTemplateSpecializationDecl* D) +{ + populate(I.Template, D, D->getSpecializedTemplate()); + populate(I, cast(D)); } void ASTVisitor:: populate( SpecializationInfo& I, - bool const isNew, ClassTemplateSpecializationDecl* D) { - if (!isNew) - { - return; - } - CXXRecordDecl const* PD = getInstantiatedFrom(D); - populate(I.Args, D->getTemplateArgs().asArray()); - generateID(PD, I.Primary); - I.Name = extractName(PD); - - linkParent(I, D); } void ASTVisitor:: populate( FriendInfo& I, - bool const isNew, FriendDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - // A NamedDecl nominated by a FriendDecl // will be one of the following: // - FunctionDecl @@ -1171,10 +1287,9 @@ populate( ND->getFriendObjectKind() == Decl::FOK_Undeclared) || (isa(ND) && ND->isFirstDecl())) { - traverseAny(ND); + traverse(cast(ND)); } } - // Since a friend declaration which name non-class types // will be ignored, a type nominated by a FriendDecl can // essentially be anything @@ -1182,30 +1297,15 @@ populate( { I.FriendType = toTypeInfo(TSI->getType()); } - - linkParent(I, D); } void ASTVisitor:: populate( GuideInfo& I, - bool const isNew, CXXDeductionGuideDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - - // deduction guides cannot be redeclared, so there is nothing to merge - if (!isNew) - { - return; - } - - I.Name = extractName(D->getDeducedTemplate()); - I.Deduced = toTypeInfo(D->getReturnType()); - for (const ParmVarDecl* P : D->parameters()) { I.Params.emplace_back( @@ -1214,132 +1314,60 @@ populate( // deduction guides cannot have default arguments std::string()); } - populate(I.Explicit, D->getExplicitSpecifier()); +} - linkParent(I, D); +void +ASTVisitor:: +populate( + GuideInfo& I, + FunctionTemplateDecl* D) +{ + populate(I.Template, D->getTemplatedDecl(), D); + populate(I, cast(D->getTemplatedDecl())); } void ASTVisitor:: populate( NamespaceAliasInfo& I, - bool const isNew, NamespaceAliasDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - - I.Name = extractName(D); - auto const& Underlying = I.AliasedSymbol = - std::make_unique(); - - NamedDecl* Aliased = D->getAliasedNamespace(); - Underlying->Name = Aliased->getIdentifier()->getName(); - upsertDependency(Aliased, Underlying->id); - if (NestedNameSpecifier const* NNS = D->getQualifier()) - { - Underlying->Prefix = toNameInfo(NNS); - } - - linkParent(I, D); + NamedDecl const* Aliased = D->getAliasedNamespace(); + I.AliasedSymbol = toNameInfo(Aliased); } void ASTVisitor:: populate( UsingInfo& I, - bool const isNew, UsingDecl* D) { - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - - if (!isNew) - { - return; - } - - I.Name = extractName(D); I.Class = UsingClass::Normal; I.Qualifier = toNameInfo(D->getQualifier()); - - for (auto const* UDS: D->shadows()) - { - upsertDependency( - UDS->getTargetDecl(), - I.UsingSymbols.emplace_back()); - } - linkParent(I, D); -} - -void -ASTVisitor:: -populate( - ConceptInfo& I, - bool const isNew, - ConceptDecl* D) -{ - bool const documented = generateJavadoc(I.javadoc, D); - populate(I, D->getBeginLoc(), true, documented); - if (!isNew) - { - return; - } - - I.Name = extractName(D); - populate(I.Constraint, D->getConstraintExpr()); - - linkParent(I, D); -} - -void -ASTVisitor:: -populate( - SourceInfo& I, - clang::SourceLocation const loc, - bool const definition, - bool const documented) -{ - unsigned line = source_.getPresumedLoc( - loc, false).getLine(); - FileInfo* file = findFileInfo(loc); - MRDOCS_ASSERT(file); - - if (definition) - { - if (I.DefLoc) - { - return; - } - I.DefLoc.emplace(file->full_path, - file->short_path, line, file->kind, - documented); - } - else + for (UsingShadowDecl const* UDS: D->shadows()) { - auto const existing = std::ranges:: - find_if(I.Loc, - [line, file](const Location& l) - { - return l.LineNumber == line && - l.Path == file->full_path; - }); - if (existing != I.Loc.end()) + ScopeExitRestore s(mode_, ExtractionMode::Dependency); + Decl* S = UDS->getTargetDecl(); + Info* SI = findOrTraverse(S); + if (SI) { - return; + I.UsingSymbols.emplace_back(SI->id); } - I.Loc.emplace_back(file->full_path, - file->short_path, line, file->kind, - documented); } } +void +ASTVisitor:: +populate( + ConceptInfo& I, + ConceptDecl* D) +{ + populate(I.Template, D, D); + populate(I.Constraint, D->getConstraintExpr()); +} + + /* Default function to populate template parameters */ template < @@ -1358,30 +1386,40 @@ void ASTVisitor:: populate( TemplateInfo& Template, - CXXRecordDeclTy* D, + CXXRecordDeclTy*, ClassTemplateDecl* CTD) { MRDOCS_ASSERT(CTD); + populate(Template, CTD->getTemplateParameters()); +} - // If D is a partial/explicit specialization, extract the template arguments - if (auto* CTSD = dyn_cast(D)) - { - generateID(getInstantiatedFrom(CTD), Template.Primary); +void +ASTVisitor:: +populate( + TemplateInfo& Template, + ClassTemplateSpecializationDecl* CTSD, + ClassTemplateDecl* CTD) +{ + MRDOCS_ASSERT(CTD); - // Extract the template arguments of the specialization - populate(Template.Args, CTSD->getTemplateArgsAsWritten()); + // If D is a partial/explicit specialization, extract the template arguments + generateID(getInstantiatedFrom(CTD), Template.Primary); - // Extract the template parameters if this is a partial specialization - if (auto* CTPSD = dyn_cast(D)) - { - populate(Template, CTPSD->getTemplateParameters()); - } + // Extract the template arguments of the specialization + if (const auto* argsAsWritten = CTSD->getTemplateArgsAsWritten()) { + populate(Template.Args, argsAsWritten); + } else { + // Implicit specializations do not have template arguments as written + populate(Template.Args, CTSD->getTemplateArgs().asArray()); } - else + + // Extract the template parameters if this is a partial specialization + if (auto* CTPSD = dyn_cast(CTSD)) { - // Otherwise, extract the template parameter list from CTD - populate(Template, CTD->getTemplateParameters()); + TemplateParameterList* params = CTPSD->getTemplateParameters(); + populate(Template, params); } + } template VarDeclTy> @@ -1420,7 +1458,7 @@ populate( { MRDOCS_ASSERT(FPT); I.Implicit = ! FPT->hasNoexceptExceptionSpec(); - I.Kind = convertToNoexceptKind( + I.Kind = toNoexceptKind( FPT->getExceptionSpecType()); // store the operand, if any if (Expr const* NoexceptExpr = FPT->getNoexceptExpr()) @@ -1436,7 +1474,7 @@ populate( const ExplicitSpecifier& ES) { I.Implicit = ! ES.isSpecified(); - I.Kind = convertToExplicitKind(ES); + I.Kind = toExplicitKind(ES); // store the operand, if any if (Expr const* ExplicitExpr = ES.getExpr()) @@ -1591,6 +1629,10 @@ populate( TemplateInfo& TI, TemplateParameterList const* TPL) { + if (!TPL) + { + return; + } if (TPL->size() > TI.Params.size()) { TI.Params.resize(TPL->size()); @@ -1611,6 +1653,10 @@ populate( std::vector>& result, const ASTTemplateArgumentListInfo* args) { + if (!args) + { + return; + } return populate(result, std::views::transform(args->arguments(), [](auto& x) -> auto& @@ -1668,7 +1714,7 @@ extractName(DeclarationName const N) } case DeclarationName::CXXOperatorName: { - OperatorKind const K = convertToOperatorKind( + OperatorKind const K = toOperatorKind( N.getCXXOverloadedOperator()); result.append("operator"); std::string_view const name = getOperatorName(K); @@ -1688,119 +1734,6 @@ extractName(DeclarationName const N) return result; } -void -ASTVisitor:: -linkParent( - Info& I, - Decl* D) -{ - // Find the parent DeclContext - Decl* PD = getParent(D); - auto const ParentID = upsertParent(PD, I); - MRDOCS_ASSERT(find(ParentID)); - I.Parent = ParentID; -} - -SymbolID -ASTVisitor:: -upsertParent(Decl* PD, Info& I) -{ - // AFREITAS: this function should eventually - // be replaced with a simple call to upsert(Decl*) - // and another call to addMember(ScopeInfo&, Info&) - - SymbolID const ParentID = generateID(PD); - - // Ensure the Info object for the parent exists - // and ensure D is added to the parent's members - switch (PD->getKind()) - { - // The TranslationUnit DeclContext is the global namespace; - // it uses SymbolID::global and should *always* exist - case Decl::TranslationUnit: - { - MRDOCS_ASSERT(ParentID == SymbolID::global); - auto [P, isNew] = upsert(ParentID); - addMember(P, I); - break; - } - case Decl::Namespace: - { - auto [P, isNew] = upsert(ParentID); - populate(P, isNew, cast(PD)); - addMember(P, I); - break; - } - // special case for an explicit specializations of - // a member of an implicit instantiation. - case Decl::ClassTemplateSpecialization: - case Decl::ClassTemplatePartialSpecialization: - if (auto* S = dyn_cast(PD); - S && S->getSpecializationKind() == TSK_ImplicitInstantiation) - { - // KRYSTIAN FIXME: i'm pretty sure DeclContext::getDeclKind() - // will never be Decl::ClassTemplatePartialSpecialization for - // implicit instantiations; instead, the - // ClassTemplatePartialSpecializationDecl is accessible through - // S->getSpecializedTemplateOrPartial if the implicit instantiation - // used a partially specialized template, - MRDOCS_ASSERT( - PD->getKind() != Decl::ClassTemplatePartialSpecialization); - auto [P, isNew] = upsert(ParentID); - populate(P, isNew, S); - addMember(P, I); - break; - } - // non-implicit instantiations should be - // treated like normal CXXRecordDecls - [[fallthrough]]; - // we should never encounter a Record - // that is not a CXXRecord - case Decl::CXXRecord: - { - auto [P, isNew] = upsert(ParentID); - populate(P, isNew, cast(PD)); - addMember(P, I); - break; - } - case Decl::Enum: - { - auto [P, isNew] = upsert(ParentID); - populate(P, isNew, cast(PD)); - addMember(P, I); - break; - } - default: - MRDOCS_UNREACHABLE(); - } - - return ParentID; -} - - -void -ASTVisitor:: -addMember( - ScopeInfo& P, - Info& C) -{ - // Include C.id in P.Members if it's not already there - if (bool const exists = std::ranges::find(P.Members, C.id) - != P.Members.end(); - !exists) - { - P.Members.emplace_back(C.id); - } - - // Include C.id in P.Lookups[C.Name] if it's not already there - auto& lookups = P.Lookups.try_emplace(C.Name).first->second; - if (bool const exists = std::ranges::find(lookups, C.id) != lookups.end(); - !exists) - { - lookups.emplace_back(C.id); - } -} - bool ASTVisitor:: generateJavadoc( @@ -1832,16 +1765,17 @@ generateJavadoc( std::unique_ptr ASTVisitor:: -toTypeInfo( - QualType const qt, - ExtractMode const extract_mode) +toTypeInfo(QualType const qt) { - // extract_mode is only used during the extraction - // the terminal type & its parents; the extraction of - // function parameters, template arguments, and the parent class - // of member pointers is done in ExtractMode::IndirectDependency - ExtractionScope scope = enterMode(extract_mode); - // build the TypeInfo representation for the type + MRDOCS_SYMBOL_TRACE(qt, context_); + + // The qualified symbol referenced by a regular symbol is a dependency. + // For library types, can be proved wrong and the Info type promoted + // to a regular type later on if the type matches the regular + // extraction criteria + ScopeExitRestore s(mode_, ExtractionMode::Dependency); + + // Build the TypeInfo representation for the type TypeInfoBuilder Builder(*this); Builder.Visit(qt); return Builder.result(); @@ -1850,23 +1784,17 @@ toTypeInfo( std::unique_ptr ASTVisitor:: toNameInfo( - NestedNameSpecifier const* NNS, - ExtractMode const extract_mode) + NestedNameSpecifier const* NNS) { - ExtractionScope scope = enterMode(extract_mode); - - std::unique_ptr I = nullptr; if (!NNS) { - return I; - } - - if (checkSpecialNamespace(I, NNS, nullptr)) - { - return I; + return nullptr; } + MRDOCS_SYMBOL_TRACE(NNS, context_); - if(const Type* T = NNS->getAsType()) + ScopeExitRestore scope(mode_,ExtractionMode::Dependency); + std::unique_ptr I = nullptr; + if (const Type* T = NNS->getAsType()) { NameInfoBuilder Builder(*this, NNS->getPrefix()); Builder.Visit(T); @@ -1876,21 +1804,29 @@ toNameInfo( { I = std::make_unique(); I->Name = II->getName(); - I->Prefix = toNameInfo(NNS->getPrefix(), extract_mode); + I->Prefix = toNameInfo(NNS->getPrefix()); } else if(const NamespaceDecl* ND = NNS->getAsNamespace()) { I = std::make_unique(); I->Name = ND->getIdentifier()->getName(); - upsertDependency(ND, I->id); - I->Prefix = toNameInfo(NNS->getPrefix(), extract_mode); + I->Prefix = toNameInfo(NNS->getPrefix()); + Decl const* ID = getInstantiatedFrom(ND); + if (Info* info = findOrTraverse(const_cast(ID))) + { + I->id = info->id; + } } else if(const NamespaceAliasDecl* NAD = NNS->getAsNamespaceAlias()) { I = std::make_unique(); I->Name = NAD->getIdentifier()->getName(); - upsertDependency(NAD, I->id); - I->Prefix = toNameInfo(NNS->getPrefix(), extract_mode); + I->Prefix = toNameInfo(NNS->getPrefix()); + Decl const* ID = getInstantiatedFrom(NAD); + if (Info* info = findOrTraverse(const_cast(ID))) + { + I->id = info->id; + } } return I; } @@ -1901,8 +1837,7 @@ ASTVisitor:: toNameInfo( DeclarationName const Name, std::optional TArgs, - NestedNameSpecifier const* NNS, - ExtractMode const extract_mode) + NestedNameSpecifier const* NNS) { if (Name.isEmpty()) { @@ -1922,7 +1857,7 @@ toNameInfo( I->Name = extractName(Name); if (NNS) { - I->Prefix = toNameInfo(NNS, extract_mode); + I->Prefix = toNameInfo(NNS); } return I; } @@ -1933,21 +1868,25 @@ ASTVisitor:: toNameInfo( Decl const* D, std::optional TArgs, - NestedNameSpecifier const* NNS, - ExtractMode extract_mode) + NestedNameSpecifier const* NNS) { const auto* ND = dyn_cast_if_present(D); if (!ND) { return nullptr; } - auto I = toNameInfo(ND->getDeclName(), - std::move(TArgs), NNS, extract_mode); + auto I = toNameInfo( + ND->getDeclName(), std::move(TArgs), NNS); if (!I) { return nullptr; } - upsertDependency(getInstantiatedFrom(D), I->id); + ScopeExitRestore scope(mode_, ExtractionMode::Dependency); + auto* ID = getInstantiatedFrom(D); + if (Info const* info = findOrTraverse(const_cast(ID))) + { + I->id = info->id; + } return I; } @@ -1957,8 +1896,7 @@ ASTVisitor:: toNameInfo>( Decl const* D, std::optional> TArgs, - NestedNameSpecifier const* NNS, - ExtractMode extract_mode); + NestedNameSpecifier const* NNS); std::unique_ptr ASTVisitor:: @@ -2028,8 +1966,8 @@ toTArg(const TemplateArgument& A) if(! isa(TD) && ! isa(TD)) { - upsertDependency(getInstantiatedFrom< - NamedDecl>(TD), R->Template); + // upsertDependency(getInstantiatedFrom< + // NamedDecl>(TD), R->Template); } } else @@ -2137,7 +2075,7 @@ std::optional>> ASTVisitor:: isSFINAEType(QualType const T) { - if (!config_->detectSfinae) + if (!config_->sfinae) { return std::nullopt; } @@ -2467,14 +2405,72 @@ shouldExtract( const Decl* D, AccessSpecifier const access) { - using enum ConfigImpl::SettingsImpl::ExtractPolicy; - if (config_->inaccessibleMembers != Always) + // The translation unit is always extracted as the + // global namespace. It can't fail any of the filters + // because its qualified name is represented by the + // empty string, and it has no file associated with it. + MRDOCS_CHECK_OR(!isa(D), true); + + // Check if this kind of symbol should be extracted. + // This filters symbols supported by mrdocs and + // symbol types whitelisted in the configuration, + // such as private members and anonymous namespaces. + MRDOCS_CHECK_OR(checkTypeFilters(D, access), false); + + // In dependency mode, we don't need the file and symbol + // filters because this is a dependency of another + // declaration that passes the filters. + if (mode_ == ExtractionMode::Dependency) + { + // If the whole declaration is implicit, we should + // not promote the extraction mode to regular + // even if it passes the filters. We should + // extract `D` in dependency mode so that + // its symbol ID is available but there's + // no need to extract its members. + if (isAllImplicit(D)) + { + return true; + } + + // So, the filters are used to determine if we + // should upgrade the extraction mode already. + // This is not a scoped promotion because + // parents and members should also assume + // the same base extraction mode. + if (checkFileFilters(D) && + checkSymbolFilters(D)) + { + mode_ = ExtractionMode::Regular; + } + // But we return true either way + return true; + } + + // Check if this symbol should be extracted according + // to its location. This checks if it's in one of the + // input directories, if it matches the file patterns, + // and it's not in an excluded file. + MRDOCS_CHECK_OR(checkFileFilters(D), false); + + // Check if this symbol should be extracted according + // to its qualified name. This checks if it matches + // the symbol patterns and if it's not excluded. + MRDOCS_CHECK_OR(checkSymbolFilters(D), false); + + return true; +} + +bool +ASTVisitor:: +checkTypeFilters(Decl const* D, AccessSpecifier access) +{ + if (!config_->privateMembers) { // KRYSTIAN FIXME: this doesn't handle direct // dependencies on inaccessible declarations MRDOCS_CHECK_OR( - access != AccessSpecifier::AS_private && - access != AccessSpecifier::AS_protected, false); + access != AccessSpecifier::AS_private, false); } // Don't extract anonymous unions @@ -2485,55 +2481,18 @@ shouldExtract( // (except for IndirectFieldDecls) MRDOCS_CHECK_OR(!D->isImplicit() || isa(D), false); - // Don't extract declarations that fail the input filter - if (!config_->input.include.empty()) - { - // Get filename - FileInfo* file = findFileInfo(D->getBeginLoc()); - MRDOCS_CHECK_OR(file, false); - std::string filename = file->full_path; - MRDOCS_CHECK_OR(std::ranges::any_of( - config_->input.include, - [&filename](const std::string& prefix) - { - return files::startsWith(filename, prefix); - }), false); - } - - // Don't extract declarations that fail the file pattern filter - if (!config_->input.filePatterns.empty()) - { - // Get filename - FileInfo* file = findFileInfo(D->getBeginLoc()); - MRDOCS_CHECK_OR(file, false); - std::string filename = file->full_path; - MRDOCS_CHECK_OR(std::ranges::any_of( - config_->input.filePatterns, - [&filename](const std::string& pattern) - { - return globMatch(pattern, filename); - }), false); - } - - // Don't extract declarations that fail the symbol filter - MRDOCS_CHECK_OR( - currentMode() != ExtractMode::Normal || - inExtractedFile(D), false); - // Don't extract anonymous namespaces unless configured to do so - if(const auto* ND = dyn_cast(D); + // and the current mode is normal + if (const auto* ND = dyn_cast(D); ND && ND->isAnonymousNamespace() && - config_->anonymousNamespaces != Always) + config_->anonymousNamespaces) { - // Always skip anonymous namespaces if so configured - MRDOCS_CHECK_OR(config_->anonymousNamespaces != Never, false); - // Otherwise, skip extraction if this isn't a dependency // KRYSTIAN FIXME: is this correct? a namespace should not // be extracted as a dependency (until namespace aliases and // using directives are supported) - MRDOCS_CHECK_OR(currentMode() != ExtractMode::Normal, false); + MRDOCS_CHECK_OR(mode_ == ExtractionMode::Regular, false); } return true; @@ -2541,223 +2500,178 @@ shouldExtract( bool ASTVisitor:: -checkSymbolFilter(NamedDecl const* ND) +checkFileFilters(Decl const* D) { - if (currentMode() != ExtractMode::Normal || - symbolFilter_.detached) + clang::SourceLocation Loc = D->getBeginLoc(); + if (Loc.isInvalid()) { - return true; + Loc = D->getLocation(); } + FileInfo* fileInfo = findFileInfo(Loc); + MRDOCS_CHECK_OR(fileInfo, false); - std::string const name = extractName(ND); - const FilterNode* parent = symbolFilter_.current; - if(const FilterNode* child = parent->findChild(name)) - { - // if there is a matching node, skip extraction if it's - // explicitly excluded AND has no children. the presence - // of child nodes indicates that some child exists that - // is explicitly whitelisted - if (child->Explicit && - child->Excluded && - child->isTerminal()) - { - return false; - } - symbolFilter_.setCurrent(child, false); - } - else - { - // if there was no matching node, check the most - // recently entered explicitly specified parent node. - // if it's blacklisted, then the "filtering default" - // is to exclude symbols unless a child is explicitly - // whitelisted - if (symbolFilter_.last_explicit - && symbolFilter_.last_explicit->Excluded) - { - return false; - } + // Check pre-processed file filters + MRDOCS_CHECK_OR(!fileInfo->passesFilters, *fileInfo->passesFilters); - if (const auto* DC = dyn_cast(ND); - !DC || - !DC->isInlineNamespace()) - { - // if this namespace does not match a child - // of the current filter node, set the detached flag - // so we don't update the namespace filter state - // while traversing the children of this namespace - symbolFilter_.detached = true; - } - } - return true; + // Call the non-cached version of this function + bool const ok = checkFileFilters(fileInfo->full_path); + fileInfo->passesFilters.emplace(ok); + return *fileInfo->passesFilters; } -// This also sets IsFileInRootDir bool ASTVisitor:: -inExtractedFile( - const Decl* D) +checkFileFilters(std::string_view const symbolPath) const { - namespace path = llvm::sys::path; - - if(const auto* ND = dyn_cast(D)) + // Don't extract declarations that fail the input filter + auto startsWithSymbolPath = [&](std::string const& inputDir) { - // out-of-line declarations require us to rebuild - // the symbol filtering state - if(ND->isOutOfLine()) - { - symbolFilter_.setCurrent( - &symbolFilter_.root, false); + return files::startsWith(symbolPath, inputDir); + }; + if (config_->recursive) + { + MRDOCS_CHECK_OR( + config_->input.empty() || + std::ranges::any_of(config_->input, startsWithSymbolPath), + false); + } + else + { + MRDOCS_CHECK_OR( + config_->input.empty() || + std::ranges::any_of(config_->input, + [symbolParentDir = files::getParentDir(symbolPath)] + (std::string const& inputDir) + { + return inputDir == symbolParentDir; + }), + false); + } - // collect all parent classes/enums/namespaces - llvm::SmallVector parents; - const Decl* P = ND; - while((P = getParent(P))) + // Don't extract declarations that fail the exclude filter + MRDOCS_CHECK_OR( + config_->exclude.empty() || + std::ranges::none_of(config_->exclude, startsWithSymbolPath), + false); + + // Don't extract declarations that fail the exclude pattern filter + MRDOCS_CHECK_OR( + config_->excludePatterns.empty() || + std::ranges::none_of(config_->excludePatterns, + [&](PathGlobPattern const& pattern) { - if (isa(P)) - { - break; - } - parents.push_back(cast(P)); - } + return pattern.match(symbolPath); + }), + false); - // check whether each parent passes the symbol filters - // as-if the declaration was inline - for(const auto* PND : std::views::reverse(parents)) + // Don't extract declarations that fail the file pattern filter + MRDOCS_CHECK_OR( + config_->filePatterns.empty() || + std::ranges::any_of(config_->filePatterns, + [symbolFilename = files::getFileName(symbolPath)] + (PathGlobPattern const& pattern) { - if (!checkSymbolFilter(PND)) - { - return false; - } - } - } + return pattern.match(symbolFilename); + }), + false); - if (!checkSymbolFilter(ND)) - { - return false; - } - } + return true; +} - FileInfo const* file = findFileInfo(D->getBeginLoc()); - if (!file && isa(D)) - { - // TranslationUnitDecl is a special case - // that doesn't have a valid SourceLocation - return true; - } - // KRYSTIAN NOTE: I'm unsure under what conditions - // this assert would fire. - MRDOCS_ASSERT(file); - // only extract from files in source root - return file->kind == FileKind::Source; +bool +ASTVisitor:: +checkSymbolFilters(Decl const* D) const +{ + // If not a NamedDecl, then symbol filters don't apply + const auto* ND = dyn_cast(D); + MRDOCS_CHECK_OR(ND, true); + return checkSymbolFilters(ND, isa(D)); } bool ASTVisitor:: -isInSpecialNamespace( - const Decl* D, - std::span const Patterns) +checkSymbolFilters(NamedDecl const* ND, bool const isScope) const { - // Check if a Decl is in a special namespace - // A Decl is in a special namespace if any of its - // parent namespaces match a special namespace pattern - if (!D || Patterns.empty()) - { - return false; - } - const DeclContext* DC = isa(D) ? - dyn_cast(D) : D->getDeclContext(); - for(; DC; DC = DC->getParent()) - { - const auto* ND = dyn_cast(DC); - if (!ND) - { - continue; - } - for (auto const& Pattern: Patterns) - { - if (Pattern.matches(ND->getQualifiedNameAsString())) - { - return true; - } - } - } - return false; + SmallString<256> const name = qualifiedName(ND); + return checkSymbolFilters(name.str(), isScope); } bool ASTVisitor:: -isInSpecialNamespace( - const NestedNameSpecifier* NNS, - std::span Patterns) +checkSymbolFilters(std::string_view const symbolName, bool const isScope) const { - // Check if a NestedNameSpecifier is in a special namespace - // It's in a special namespace if any of its prefixes are - // in a special namespace - const NamedDecl* ND = nullptr; - while(NNS) + if (isScope) { - if ((ND = NNS->getAsNamespace())) - { - break; - } - if ((ND = NNS->getAsNamespaceAlias())) - { - break; - } - NNS = NNS->getPrefix(); + return checkSymbolFiltersImpl(symbolName); } - return ND && isInSpecialNamespace(ND, Patterns); + return checkSymbolFiltersImpl(symbolName); } +template bool ASTVisitor:: -checkSpecialNamespace( - std::unique_ptr& I, - const NestedNameSpecifier* NNS, - const Decl* D) const +checkSymbolFiltersImpl(std::string_view const symbolName) const { - // Check if a NestedNameSpecifier or a Decl is in a special namespace - // If so, update the NameInfo's Name to reflect this - if (isInSpecialNamespace(NNS, config_->seeBelowFilter) || - isInSpecialNamespace(D, config_->seeBelowFilter)) + // Don't extract declarations that fail the symbol filter + auto includeMatchFn = [&](SymbolGlobPattern const& pattern) { - I = std::make_unique(); - I->Name = "see-below"; - return true; - } + if constexpr (isScope) + { + // If the symbol is a scope, such as a namespace or class, + // we want to know if symbols in that scope might match + // the filters rather than the scope symbol itself. + // Because if symbols in that scope match the filter, we also + // want to extract the scope itself. + // Thus, we only need to show we might potentially match one + // of the prefixes of the symbol patterns, not the entire + // symbol pattern for the escope. + return pattern.matchPatternPrefix(symbolName); + } + else + { + return pattern.match(symbolName); + } + }; + MRDOCS_CHECK_OR( + config_->includeSymbols.empty() || + std::ranges::any_of(config_->includeSymbols, includeMatchFn), false); - if(isInSpecialNamespace(NNS, config_->implementationDefinedFilter) || - isInSpecialNamespace(D, config_->implementationDefinedFilter)) + // Don't extract declarations that fail the exclude symbol filter + auto excludeMatchFn = [&](SymbolGlobPattern const& pattern) { - I = std::make_unique(); - I->Name = "implementation-defined"; - return true; - } + // Unlike the include filter, we want to match the entire symbol name + // for the exclude filter regardless of whether the symbol is a scope. + // If the scope is explicitly excluded, we already know we want to + // exclude all symbols in that scope + return pattern.match(symbolName); + }; + MRDOCS_CHECK_OR( + config_->excludeSymbols.empty() || + std::ranges::none_of(config_->excludeSymbols, excludeMatchFn), false); - return false; + return true; } -bool +SmallString<256> ASTVisitor:: -checkSpecialNamespace( - std::unique_ptr& I, - const NestedNameSpecifier* NNS, - const Decl* D) const +qualifiedName(Decl const* D) const { - // Check if a NestedNameSpecifier or a Decl is in a special namespace - // If so, update the TypeInfo's Name to reflect this - if (std::unique_ptr Name; - checkSpecialNamespace(Name, NNS, D)) + if (auto* ND = dyn_cast(D)) { - auto T = std::make_unique(); - T->Name = std::move(Name); - I = std::move(T); - return true; + return qualifiedName(ND); } - return false; + return {}; } +SmallString<256> +ASTVisitor:: +qualifiedName(NamedDecl const* ND) const +{ + SmallString<256> name; + llvm::raw_svector_ostream stream(name); + getQualifiedName(ND, stream, context_.getPrintingPolicy()); + return name; +} Info* ASTVisitor:: @@ -2770,6 +2684,16 @@ find(SymbolID const& id) return nullptr; } +Info* +ASTVisitor:: +find(Decl* D) +{ + auto ID = generateID(D); + MRDOCS_CHECK_OR(ID, nullptr); + return find(ID); +} + + // KRYSTIAN NOTE: we *really* should not have a // type named "SourceLocation"... ASTVisitor::FileInfo* @@ -2821,14 +2745,21 @@ upsert(SymbolID const& id) { info = info_.emplace(std::make_unique< InfoTy>(id)).first->get(); - info->Implicit = currentMode() != ExtractMode::Normal; + info->Extraction = mostSpecific(info->Extraction, mode_); } MRDOCS_ASSERT(info->Kind == InfoTy::kind_id); return {static_cast(*info), isNew}; } -template DeclType> -Expected>> +template < + class InfoTy, + HasInfoTypeFor DeclType> +Expected< + ASTVisitor::upsertResult< + std::conditional_t< + std::same_as, + InfoTypeFor_t, + InfoTy>>> ASTVisitor:: upsert(DeclType* D) { @@ -2840,91 +2771,29 @@ upsert(DeclType* D) SymbolID const id = generateID(D); MRDOCS_CHECK_MSG(id, "Failed to extract symbol ID"); - auto [I, isNew] = upsert>(id); - I.Access = convertToAccessKind(access); - - using R = upsertResult>; - return R{std::ref(I), isNew}; -} - -void -ASTVisitor:: -upsertDependency(Decl* D, SymbolID& id) -{ - if (TemplateDecl* TD = D->getDescribedTemplate()) - { - D = TD; - } - - if (D->isImplicit() || isa(D) - || isa(D)) - { - return; - } - - id = generateID(D); - - // Don't register a dependency if we never extract them - if (config_->referencedDeclarations - == ConfigImpl::SettingsImpl::ExtractPolicy::Never) - { - return; - } - - if(config_->referencedDeclarations == - ConfigImpl::SettingsImpl::ExtractPolicy::Dependency) - { - if (currentMode() != ExtractMode::DirectDependency) - { - return; - } - } - - // If it was already extracted, we are done - if (find(id)) - { - return; - } - - // FIXME: we need to handle member specializations correctly. - // we do not want to extract all members of the enclosing - // implicit instantiation - Decl* Outer = D; - DeclContext* DC = D->getDeclContext(); - do - { - if (DC->isFileContext() || - DC->isFunctionOrMethod()) - { - break; - } - - if (auto const* RD = dyn_cast(DC); - RD && - RD->isAnonymousStructOrUnion()) - { - continue; - } - - // when extracting dependencies, we want to extract - // all members of the containing class, not just this one - if (auto* TD = dyn_cast(DC)) - { - Outer = TD; - } - } - while((DC = DC->getParent())); - - if (TemplateDecl* TD = Outer->getDescribedTemplate()) - { - Outer = TD; - } - - // Add the adjusted declaration to the set of dependencies - if (!isa(Outer)) - { - dependencies_.insert(Outer); - } + using R = std::conditional_t< + std::same_as, + InfoTypeFor_t, + InfoTy>; + auto [I, isNew] = upsert(id); + I.Access = toAccessKind(access); + + // If the symbol was previously extracted as a dependency + // and is now being extracted as a regular symbol because + // it passed the more constrained filters, update the + // extraction mode and set the symbol as new so it's populated + // this time. + bool const previouslyExtractedAsDependency = + !isNew && + mode_ != ExtractionMode::Dependency && + I.Extraction == ExtractionMode::Dependency; + if (previouslyExtractedAsDependency) + { + I.Extraction = mostSpecific(I.Extraction, mode_); + isNew = true; + } + + return upsertResult{std::ref(I), isNew}; } } // clang::mrdocs diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index 51d6c09edf..3f469651d0 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -16,11 +16,12 @@ #include "lib/Lib/ConfigImpl.hpp" #include "lib/Lib/ExecutionContext.hpp" -#include "lib/AST/SymbolFilter.hpp" #include "lib/AST/ClangHelpers.hpp" +#include #include #include #include +#include #include #include #include @@ -78,16 +79,6 @@ class ASTVisitor // An unordered set of all extracted Info declarations InfoSet info_; - /* The set of dependencies found during extraction - - The metadata for these dependencies will be extracted - after the initial extraction pass, if the configuration - option `referencedDeclarations` is not set to `never`. - - @see @ref buildDependencies - */ - std::unordered_set dependencies_; - /* Struct to hold pre-processed file information. This struct stores information about a file, including its full path, @@ -112,7 +103,10 @@ class ASTVisitor std::string short_path; // The kind of the file. - FileKind kind; + FileKind kind = FileKind::Source; + + // Whether this file passes the file filters + std::optional passesFilters; }; /* A map of Clang FileEntry objects to Visitor FileInfo objects @@ -131,59 +125,28 @@ class ASTVisitor */ std::unordered_map files_; - /* The filter for symbols to extract - - Whenever traversing a declaration, the ASTVisitor - will check if the declaration passes the filter - before extracting metadata for it. - */ - SymbolFilter symbolFilter_; - - enum class ExtractMode - { - // Extraction of declarations which pass all filters - Normal, - // Extraction of declarations as direct dependencies - DirectDependency, - // Extraction of declarations as indirect dependencies - IndirectDependency, - }; - - // The current extraction mode - ExtractMode mode = ExtractMode::Normal; + /* The current extraction mode - struct [[nodiscard]] ExtractionScope - { - ASTVisitor& visitor_; - ExtractMode previous_; - - public: - ExtractionScope( - ASTVisitor& visitor, - ExtractMode mode) noexcept - : visitor_(visitor) - , previous_(visitor.mode) - { - visitor_.mode = mode; - } + This defines the extraction mode assigned to + new Info types. - ~ExtractionScope() - { - visitor_.mode = previous_; - } - }; + A symbol that passes the filters will be + extracted as a regular symbol. - ExtractionScope - enterMode(ExtractMode new_mode) noexcept - { - return {*this, new_mode}; - } + If a symbol also passes the see-below or + implementation-defined filters, we + change the current extraction mode + accordingly so the symbol can be tagged. + Members of this symbol types are not + extracted. - ExtractMode - currentMode() const noexcept - { - return mode; - } + A symbol that doesn't pass the filters + can only be extracted as a dependency. + A symbol is extracted as a dependency + when another symbol makes a reference + to it. + */ + ExtractionMode mode_ = ExtractionMode::Regular; struct SFINAEInfo { @@ -197,7 +160,6 @@ class ASTVisitor InfoTy& I; bool isNew; }; - public: /** Constructor for ASTVisitor. @@ -258,68 +220,10 @@ class ASTVisitor } private: - /* Build metadata representation for all dependencies in the AST - - If the option `referencedDeclarations` is not set to `never`, - this function will be called after the initial extraction - pass to extract metadata for all referenced declarations - in the main symbols extracted. - - It calls `traverseAny` for each declaration in the set - of dependencies, which will recursively traverse the - declaration and extract the relevant information. - - If this pass finds new dependencies, it will call itself - recursively to extract the metadata for those dependencies - until no new dependencies are found. - */ - void - buildDependencies(); - // ================================================= // AST Traversal // ================================================= - /* Traverse a declaration - - This function is called to traverse any declaration. - The Decl element is converted to its derived type, - and the appropriate `traverse` overload is called. - - The `build()` function will call this function with - `context_.getTranslationUnitDecl()` to initiate - the traversal of the entire AST, while - `buildDependencies()` will call it for each dependency. - - @param D The declaration to traverse. - */ - void - traverseAny(Decl* D); - - /* Traverse a declaration and template declaration - - This function is called to traverse any declaration - with the corresponding template declaration. - - The Decl element is converted to its derived type, - and the appropriate `traverse` overload is called - with the template declaration as the second argument. - - Both the information about the type and the - template parameters are extracted into the - `Info` object. - - The `traverseAny()` function will call this function - for each declaration with a corresponding template - declaration. - - @param D The declaration context to traverse. - @param TD The corresponding template declaration. - */ - template TemplateDeclTy> - void - traverseAny(Decl* D, TemplateDeclTy* TD); - /* The default implementation for the traverse function This function defines the usual behavior for the @@ -329,71 +233,28 @@ class ASTVisitor declaration, populates it with the necessary information, and optionally traverses the members of the declaration. - @param D The declaration to traverse - */ - template - requires std::derived_from - void - traverse(DeclTy* D) - { - defaultTraverseImpl(D); - } - - /* The default implementation for the traverse function with a - template parameter - - This function defines the usual behavior for the - traverse function for a concrete declaration type - when associated with a template declaration. - - It creates a new corresponding Info object for the - declaration, populates it with the necessary information, - populates the Template information, and optionally - traverses the members of the declaration. + @tparam InfoTy The type of Info object to create. If the + default type is used, the function will determine the + appropriate Info type based on the declaration type. + @tparam DeclTy The type of the declaration to traverse. @param D The declaration to traverse - @param TD The template declaration associated with `D` */ template < - std::derived_from DeclTy, - std::derived_from TemplateDeclTy> - void - traverse(DeclTy* D, TemplateDeclTy* TD) - { - defaultTraverseImpl(D, TD); - } - - /* Default implementation for the traverse function - - This function defines the common logic for - `traverse(DeclTy*)` and `traverse(DeclTy*, TemplateDeclTy*)`, - where the `PopulateFromTD` parameter determines whether - the template information should be populated from the - template declaration. - */ - template < - bool PopulateFromTD, - std::derived_from DeclTy, - std::derived_from TemplateDeclTy = TemplateDecl> - void - defaultTraverseImpl(DeclTy* D, TemplateDeclTy* TD = nullptr); - - /* Traverse a C++ function or function specialization + class InfoTy = void, + std::derived_from DeclTy> + Info* + traverse(DeclTy* D); - This handles `FunctionDecl` and `CXXMethodDecl`. + /* Traverse a function template - Both these classes can return `true` for - `D->isFunctionTemplateSpecialization()`, - in which case the function will also populate the - template parameters of the function. + This function redirects the traversal + so that it can be handled as a + concept or function template. */ - template - requires - std::derived_from && - std::derived_from - void - traverse(FunctionDeclTy* D); + Info* + traverse(FunctionTemplateDecl* D); /* Traverse a using directive @@ -403,7 +264,7 @@ class ASTVisitor If the parent declaration is a Namespace, we update its `UsingDirectives` field. */ - void + Info* traverse(UsingDirectiveDecl* D); /* Traverse a member of an anonymous union. @@ -411,7 +272,7 @@ class ASTVisitor We get the anonymous union field and traverse it as a regular `FieldDecl`. */ - void + Info* traverse(IndirectFieldDecl*); // ================================================= @@ -426,9 +287,45 @@ class ASTVisitor The function will call traverseAny for all members of the declaration context. */ - template DeclTy> + template < + std::derived_from InfoTy, + std::derived_from DeclTy> + requires (!std::derived_from) + void + traverseMembers(InfoTy& I, DeclTy* DC); + + template < + std::derived_from InfoTy, + std::derived_from DeclTy> void - traverseMembers(DeclTy* DC); + traverseMembers(InfoTy& I, DeclTy* DC); + + /* Traverse the parents of a declaration + + This function is called to traverse the parents of + a Decl until we find the translation unit declaration + or a parent that has already been extracted. + + This function is called when the declaration is + being extracted as a dependency, and we need to + identify the parent scope of the declaration. + + For regular symbols, the parent is identified + during the normal traversal of the declaration + context. + */ + template < + std::derived_from InfoTy, + std::derived_from DeclTy> + requires (!std::derived_from) + void + traverseParents(InfoTy& I, DeclTy* DC); + + template < + std::derived_from InfoTy, + std::derived_from DeclTy> + void + traverseParents(InfoTy& I, DeclTy* DC); /* Generates a Unified Symbol Resolution value for a declaration. @@ -478,56 +375,81 @@ class ASTVisitor // ================================================= // Populate functions // ================================================= + template InfoTy, class DeclTy> + void + populateInfoBases(InfoTy& I, bool isNew, DeclTy* D); + + void + populate(SourceInfo& I, clang::SourceLocation loc, bool definition, bool documented); + void - populate(NamespaceInfo& I, bool isNew, NamespaceDecl* D); + populate(NamespaceInfo& I, NamespaceDecl* D); static void - populate(NamespaceInfo& I, bool isNew, TranslationUnitDecl* D); + populate(NamespaceInfo& I, TranslationUnitDecl* D); void - populate(RecordInfo& I, bool isNew, CXXRecordDecl* D); + populate(RecordInfo& I, CXXRecordDecl* D); + + void + populate(RecordInfo& I, ClassTemplateDecl* D); + + void + populate(RecordInfo& I, ClassTemplateSpecializationDecl* D); template DeclTy> void - populate(FunctionInfo& I, bool isNew, DeclTy* D); + populate(FunctionInfo& I, DeclTy* D); + + void + populate(FunctionInfo& I, FunctionTemplateDecl* D); void - populate(EnumInfo& I, bool isNew, EnumDecl* D); + populate(EnumInfo& I, EnumDecl* D); void - populate(EnumConstantInfo& I, bool isNew, EnumConstantDecl* D); + populate(EnumConstantInfo& I, EnumConstantDecl* D); template TypedefNameDeclTy> void - populate(TypedefInfo& I, bool isNew, TypedefNameDeclTy* D); + populate(TypedefInfo& I, TypedefNameDeclTy* D); void - populate(VariableInfo& I, bool isNew, VarDecl* D); + populate(TypedefInfo& I, TypeAliasTemplateDecl* D); void - populate(FieldInfo& I, bool isNew, FieldDecl* D); + populate(VariableInfo& I, VarDecl* D); void - populate(SpecializationInfo& I, bool isNew, ClassTemplateSpecializationDecl* D); + populate(VariableInfo& I, VarTemplateDecl* D); void - populate(FriendInfo& I, bool isNew, FriendDecl* D); + populate(VariableInfo& I, VarTemplateSpecializationDecl* D); void - populate(GuideInfo& I, bool isNew, CXXDeductionGuideDecl* D); + populate(FieldInfo& I, FieldDecl* D); void - populate(NamespaceAliasInfo& I, bool isNew, NamespaceAliasDecl* D); + populate(SpecializationInfo& I, ClassTemplateSpecializationDecl* D); void - populate(UsingInfo& I, bool isNew, UsingDecl* D); + populate(FriendInfo& I, FriendDecl* D); void - populate(ConceptInfo& I, bool isNew, ConceptDecl* D); + populate(GuideInfo& I, CXXDeductionGuideDecl* D); void - populate(SourceInfo& I, clang::SourceLocation loc, bool definition, bool documented); + populate(GuideInfo& I, FunctionTemplateDecl* D); + + void + populate(NamespaceAliasInfo& I, NamespaceAliasDecl* D); + + void + populate(UsingInfo& I, UsingDecl* D); + + void + populate(ConceptInfo& I, ConceptDecl* D); /* Default function to populate the template information @@ -550,6 +472,9 @@ class ASTVisitor void populate(TemplateInfo& Template, CXXRecordDeclTy*, ClassTemplateDecl* CTD); + void + populate(TemplateInfo& Template, ClassTemplateSpecializationDecl* D, ClassTemplateDecl* CTD); + /* Populate the template information for a variable template The function will populate the template parameters @@ -559,6 +484,19 @@ class ASTVisitor void populate(TemplateInfo& Template, VarDeclTy* D, VarTemplateDecl* VTD); + template< + std::derived_from DeclTy, + std::derived_from TemplateDeclTy> + void + populate(std::optional& Template, DeclTy* D, TemplateDeclTy* VTD) + { + if (!Template) + { + Template.emplace(); + } + populate(*Template, D, VTD); + } + void populate(NoexceptInfo& I, const FunctionProtoType* FPT); @@ -579,10 +517,19 @@ class ASTVisitor void populate(std::unique_ptr& I, const NamedDecl* N); + void + populate(std::optional& TI, const TemplateParameterList* TPL) { + if (!TI) + { + TI.emplace(); + } + populate(*TI, TPL); + } + void populate(TemplateInfo& TI, const TemplateParameterList* TPL); - template + template Range> void populate( std::vector>& result, @@ -590,13 +537,18 @@ class ASTVisitor { for (TemplateArgument const& arg : args) { + if (arg.getIsDefaulted()) + { + break; + } // KRYSTIAN NOTE: is this correct? should we have a // separate TArgKind for packs instead of "unlaminating" // them as we are doing here? if (arg.getKind() == TemplateArgument::Pack) { populate(result, arg.pack_elements()); - } else + } + else { result.emplace_back(toTArg(arg)); } @@ -619,38 +571,6 @@ class ASTVisitor std::string extractName(DeclarationName N); - /* Populate the Info.Parent of a declaration - - This function will find the parent context `P` of - `D` and then: - - @li It ensures the Info object for the parent context `P` - exists, and that `D` is included as a member of `P`. - @li It ensures the SymbolID of `P` is set as the parent - of `D`. - */ - void - linkParent(Info& I, Decl* D); - - /* Ensure parent exists and has child has member - */ - SymbolID - upsertParent(Decl* Parent, Info& Child); - - /* Emplace a member Info into a ScopeInfo - - Given a ScopeInfo `P` and an Info `C`, this function will - add the SymbolID of `C` to `P.Members` and `P.Lookups[C.Name]`. - - @param P The parent ScopeInfo - @param C The child Info - */ - static - void - addMember( - ScopeInfo& P, - Info& C); - /* Parse the comments above a declaration as Javadoc This function will parse the comments above a declaration @@ -666,30 +586,25 @@ class ASTVisitor Decl const* D); std::unique_ptr - toTypeInfo( - QualType qt, - ExtractMode extract_mode = ExtractMode::IndirectDependency); + toTypeInfo(QualType qt); std::unique_ptr toNameInfo( - NestedNameSpecifier const* NNS, - ExtractMode extract_mode = ExtractMode::IndirectDependency); + NestedNameSpecifier const* NNS); template > std::unique_ptr toNameInfo( DeclarationName Name, std::optional TArgs = std::nullopt, - const NestedNameSpecifier* NNS = nullptr, - ExtractMode extract_mode = ExtractMode::IndirectDependency); + const NestedNameSpecifier* NNS = nullptr); template > std::unique_ptr toNameInfo( const Decl* D, std::optional TArgs = std::nullopt, - const NestedNameSpecifier* NNS = nullptr, - ExtractMode extract_mode = ExtractMode::IndirectDependency); + const NestedNameSpecifier* NNS = nullptr); std::unique_ptr toTArg(const TemplateArgument& A); @@ -781,74 +696,33 @@ class ASTVisitor return shouldExtract(D, getAccess(D)); } - - // Determine if a declaration passes the symbol filter bool - checkSymbolFilter(const NamedDecl* ND); + checkTypeFilters(Decl const* D, AccessSpecifier access); - // Determine if a declaration is in a file that should be extracted bool - inExtractedFile(const Decl* D); + checkFileFilters(Decl const* D); - /* Determine if the declaration is in a special namespace - - This function will determine if a declaration is in a - special namespace based on the current symbol filter - state and the special namespace patterns. - - The function will check if the declaration is in a - special namespace, and if so, it will return true. - - @param D the declaration to check - @param Patterns the special namespace patterns - - @return true if the declaration is in a special namespace, - and false otherwise. - */ - static bool - isInSpecialNamespace( - const Decl* D, - std::span Patterns); + checkFileFilters(std::string_view symbolPath) const; - // @overload isInSpecialNamespace - static bool - isInSpecialNamespace( - const NestedNameSpecifier* NNS, - std::span Patterns); - - /* Check if a declaration is in a special namespace - - This function will check if a declaration is in a special - namespace based on the current symbol filter state and - the special namespace patterns. + checkSymbolFilters(Decl const* D) const; - If the declaration is in a special namespace, the function - will set the NameInfo `I` to the appropriate special - namespace name (see-below or implementation-defined), and - return true. - - @param I The NameInfo to set if the declaration is in a - special namespace - @param NNS the nested name specifier of the declaration - @param D the declaration to check + bool + checkSymbolFilters(NamedDecl const* ND, bool const isScope) const; - @return true if the declaration is in a special namespace, - and false otherwise. - */ bool - checkSpecialNamespace( - std::unique_ptr& I, - const NestedNameSpecifier* NNS, - const Decl* D) const; + checkSymbolFilters(std::string_view symbolName, bool const isScope) const; - // @overload checkSpecialNamespace + template bool - checkSpecialNamespace( - std::unique_ptr& I, - const NestedNameSpecifier* NNS, - const Decl* D) const; + checkSymbolFiltersImpl(std::string_view symbolName) const; + + SmallString<256> + qualifiedName(Decl const* D) const; + + SmallString<256> + qualifiedName(NamedDecl const* ND) const; // ================================================= // Element access @@ -859,6 +733,54 @@ class ASTVisitor Info* find(SymbolID const& id); + /* Get Info from ASTVisitor InfoSet + + This function will generate a symbol ID for the + declaration and call the other `find` function + to get the Info object for the declaration. + */ + Info* + find(Decl* D); + + /* Find or traverse a declaration + + This function will first attempt to find the Info + object for a declaration in the InfoSet. + + If the Info object does not exist, the function + will traverse the declaration and create a new + Info object for the declaration. + + This function is useful when we need to find the + Info object for a parameter or dependency, but + we don't want to traverse or populate the + declaration if it has already been extracted + because the context where the symbol is being + used (such as function parameters or aliases), + does not contain extra non-redundant and relevant + information about the symbol. + + Thus, it avoids circular dependencies and infinite + loops when traversing the AST. If the traversal + has been triggered by a dependency, the second + call to this function will return the Info object + that was created during the first call. + + @param D The declaration to find or traverse. + + @return a pointer to the Info object. + */ + Info* + findOrTraverse(Decl* D) + { + MRDOCS_CHECK_OR(D, nullptr); + if (auto* I = find(D)) + { + return I; + } + return traverse(D); + } + /* Get FileInfo from ASTVisitor files This function will return a pointer to a FileInfo @@ -905,29 +827,16 @@ class ASTVisitor @param D The declaration to extract */ - template DeclType> - Expected>> + template < + class InfoTy = void, + HasInfoTypeFor DeclType> + Expected< + upsertResult< + std::conditional_t< + std::same_as, + InfoTypeFor_t, + InfoTy>>> upsert(DeclType* D); - - /* Get or construct an empty Info for a dependency declaration. - - The function will determine the symbol ID for the - declaration, store it in the `id` input parameter, - and add it to the set of dependencies. - - The dependencies are processed after the initial - extraction pass, if the configuration option - `referencedDeclarations` is not set to `never`. - */ - void - upsertDependency(Decl* D, SymbolID& id); - - // @overload upsertDependency - void - upsertDependency(Decl const* D, SymbolID& id) - { - return upsertDependency(const_cast(D), id); - } }; } // clang::mrdocs diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 4df4cf0217..21101853c3 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -14,6 +14,7 @@ #include #include #include +#include namespace clang::mrdocs { @@ -242,4 +243,99 @@ getParent(Decl* D) return nullptr; } +void +getQualifiedName( + NamedDecl const* ND, + raw_ostream& stream, + const PrintingPolicy &policy) +{ + if (const auto* CTS = dyn_cast(ND)) + { + CTS->getSpecializedTemplate()->printQualifiedName(stream, policy); + TemplateArgumentList const& args = CTS->getTemplateArgs(); + stream << '<'; + for (unsigned i = 0, e = args.size(); i != e; ++i) { + if (args[i].getIsDefaulted()) + { + break; + } + if (i) + { + stream << ","; + } + args[i].print(policy, stream, true); + } + stream << '>'; + } + else + { + ND->printQualifiedName(stream, policy); + } +} + +Decl const* +decayToPrimaryTemplate(Decl const* D) +{ +#ifndef NDEBUG + SmallString<128> symbolName; + llvm::raw_svector_ostream os(symbolName); + D->print(os); + report::debug("symbolName: ", std::string_view(os.str())); +#endif + + Decl const* ID = D; + + // Check parent + if (CXXRecordDecl const* ClassParent = dyn_cast(getParent(ID))) + { + if (Decl const* DecayedClassParent = decayToPrimaryTemplate(ClassParent); + DecayedClassParent != ClassParent && + isa(DecayedClassParent)) + { + auto const* RD = dyn_cast(DecayedClassParent); + CXXRecordDecl* RDParent = RD->getTemplatedDecl(); + auto* NamedID = dyn_cast(ID); + auto NamedDecls = RDParent->decls() + | std::ranges::views::transform([](Decl* C) { return dyn_cast(C); }) + | std::ranges::views::filter([](NamedDecl* C) { return C; }); + for (NamedDecl const* Child : NamedDecls) + { + if (Child->getDeclName() == NamedID->getDeclName() && + Child->getKind() == ID->getKind()) + { + ID = Child; + break; + } + } + } + } + + // Check template specialization + if (auto const* TSD = dynamic_cast(ID); + TSD && + !TSD->isExplicitSpecialization()) + { + ID = TSD->getSpecializedTemplate(); + } + + return ID; +} + +bool +isAllImplicit(Decl const* D) +{ + if (!D) + { + return true; + } + if (auto const* TSD = dynamic_cast(D); + TSD && + TSD->isExplicitSpecialization()) + { + return false; + } + auto const* P = getParent(D); + return isAllImplicit(P); +} + } // clang::mrdocs diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 13ef637f22..12a4f040d8 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -77,15 +77,26 @@ struct InfoTypeFor : std::type_identity {}; // Extract RecordInfo from anything derived from CXXRecordDecl +// and ClassTemplateDecl. Decls derived from CXXRecordDecl +// include class specializations. template DeclType> struct InfoTypeFor : std::type_identity {}; +template <> +struct InfoTypeFor + : std::type_identity {}; + // Extract FunctionInfo from anything derived from FunctionDecl template FunctionTy> +requires (!std::same_as) struct InfoTypeFor : std::type_identity {}; +template <> +struct InfoTypeFor + : std::type_identity {}; + // Extract EnumInfo from EnumDecl template <> struct InfoTypeFor @@ -101,11 +112,20 @@ template TypedefNameTy> struct InfoTypeFor : std::type_identity {}; +template <> +struct InfoTypeFor + : std::type_identity {}; + // Extract VariableInfo from anything derived from VarDecl +// and VarTemplateDecl. template VarTy> struct InfoTypeFor : std::type_identity {}; +template <> +struct InfoTypeFor + : std::type_identity {}; + // Extract FieldInfo from FieldDecl template <> struct InfoTypeFor @@ -138,7 +158,7 @@ struct InfoTypeFor /// Determine if there's a MrDocs Info type for a Clang DeclType template -concept HasInfoTypeFor = requires +concept HasInfoTypeFor = std::derived_from && requires { typename InfoTypeFor::type; }; @@ -151,7 +171,7 @@ using InfoTypeFor_t = typename InfoTypeFor::type; */ inline AccessKind -convertToAccessKind(AccessSpecifier const spec) +toAccessKind(AccessSpecifier const spec) { switch(spec) { @@ -209,7 +229,7 @@ toConstexprKind(ConstexprSpecKind const spec) */ inline ExplicitKind -convertToExplicitKind(ExplicitSpecifier const& spec) +toExplicitKind(ExplicitSpecifier const& spec) { // no explicit-specifier if (!spec.isSpecified()) @@ -231,7 +251,7 @@ convertToExplicitKind(ExplicitSpecifier const& spec) */ inline NoexceptKind -convertToNoexceptKind(ExceptionSpecificationType const spec) +toNoexceptKind(ExceptionSpecificationType const spec) { // KRYSTIAN TODO: right now we convert pre-C++17 dynamic exception // specifications to an (roughly) equivalent noexcept-specifier @@ -263,7 +283,7 @@ convertToNoexceptKind(ExceptionSpecificationType const spec) */ inline OperatorKind -convertToOperatorKind(OverloadedOperatorKind const kind) +toOperatorKind(OverloadedOperatorKind const kind) { switch(kind) { @@ -368,7 +388,7 @@ convertToOperatorKind(OverloadedOperatorKind const kind) */ inline ReferenceKind -convertToReferenceKind(RefQualifierKind const kind) +toReferenceKind(RefQualifierKind const kind) { switch(kind) { @@ -404,7 +424,7 @@ toRecordKeyKind(TagTypeKind const kind) */ inline QualifierKind -convertToQualifierKind(unsigned const quals) +toQualifierKind(unsigned const quals) { std::underlying_type_t result = QualifierKind::None; if (quals & Qualifiers::Const) @@ -727,6 +747,104 @@ getParent(Decl const* D) { return getParent(const_cast(D)); } +MRDOCS_DECL +void +getQualifiedName( + NamedDecl const* ND, + raw_ostream &Out, + const PrintingPolicy &Policy); + +// If D refers to an implicit instantiation of a template specialization, +// decay it to the Decl of the primary template. The template arguments +// will be extracted separately as part of the TypeInfo. +// For instance, a Decl to `S<0>` becomes a Decl to `S`, unless `S<0>` is +// an explicit specialization of the primary template. +// This function also applies recursively to the parent of D so that +// the primary template is resolved for nested classes. +// For instance, a Decl to `A<0>::S` becomes a Decl to `A::S`, unless +// `A<0>` is an explicit specialization of the primary template. +MRDOCS_DECL +Decl const* +decayToPrimaryTemplate(Decl const* D); + +// Iterate the Decl and check if this is a template specialization +// also considering the parent declarations. For instance, +// S<0>::M<0> is a template specialization of S<0> and M<0>. +// This function returns true is both S<0> and M<0> are implicit +// template specializations. +MRDOCS_DECL +bool +isAllImplicit(Decl const* D); + +#ifdef NDEBUG +#define MRDOCS_SYMBOL_TRACE(D, C) +#else + +# define MRDOCS_SYMBOL_TRACE_MERGE_(a, b) a##b +# define MRDOCS_SYMBOL_TRACE_LABEL_(a) MRDOCS_SYMBOL_TRACE_MERGE_(symbol_name_, a) +# define MRDOCS_SYMBOL_TRACE_UNIQUE_NAME MRDOCS_SYMBOL_TRACE_LABEL_(__LINE__) + +namespace detail { + // concept to check if ID->printQualifiedName( + // std::declval(), + // getASTVisitor().context_.getPrintingPolicy()); + // is a valid expression + template + concept HasPrintQualifiedName = requires(T const& D, llvm::raw_svector_ostream& OS, PrintingPolicy PP) + { + D.printQualifiedName(OS, PP); + }; + + template + concept HasPrint = requires(T const& D, llvm::raw_svector_ostream& OS, PrintingPolicy PP) + { + D.print(OS, PP); + }; + + template + void + printTraceName(T const* D, ASTContext const& C, SmallString<256>& symbol_name) + { + if (!D) + { + return; + } + llvm::raw_svector_ostream os(symbol_name); + if constexpr (std::derived_from) + { + if (NamedDecl const* ND = dyn_cast(D)) + { + getQualifiedName(ND, os, C.getPrintingPolicy()); + } + else + { + os << "Decl::getDeclKindName() << ">"; + } + } + else if constexpr (HasPrintQualifiedName) + { + D->printQualifiedName(os, C.getPrintingPolicy()); + } + else if constexpr (HasPrint) + { + D->print(os, C.getPrintingPolicy()); + } + } + + template + requires (!std::is_pointer_v) + void + printTraceName(T const& D, ASTContext const& C, SmallString<256>& symbol_name) + { + printTraceName(&D, C, symbol_name); + } +} // namespace detail + +#define MRDOCS_SYMBOL_TRACE(D, C) \ + SmallString<256> MRDOCS_SYMBOL_TRACE_UNIQUE_NAME; \ + detail::printTraceName(D, C, MRDOCS_SYMBOL_TRACE_UNIQUE_NAME); \ + report::debug("{}", std::string_view(MRDOCS_SYMBOL_TRACE_UNIQUE_NAME.str())) +#endif } // clang::mrdocs diff --git a/src/lib/AST/NameInfoBuilder.cpp b/src/lib/AST/NameInfoBuilder.cpp index d64298122f..bad6d37d8c 100644 --- a/src/lib/AST/NameInfoBuilder.cpp +++ b/src/lib/AST/NameInfoBuilder.cpp @@ -18,9 +18,9 @@ namespace clang::mrdocs { void NameInfoBuilder:: buildDecltype( - const DecltypeType* T, - unsigned quals, - bool pack) + const DecltypeType*, + unsigned, + bool) { // KRYSTIAN TODO: support decltype in names // (e.g. within nested-name-specifiers). @@ -31,14 +31,9 @@ NameInfoBuilder:: buildTerminal( const NestedNameSpecifier* NNS, const Type* T, - unsigned quals, - bool pack) + unsigned, + bool) { - if (getASTVisitor().checkSpecialNamespace(Result, NNS, nullptr)) - { - return; - } - auto I = std::make_unique(); I->Name = getASTVisitor().toString(T); Result = std::move(I); @@ -54,15 +49,9 @@ buildTerminal( const NestedNameSpecifier* NNS, const IdentifierInfo* II, std::optional> TArgs, - unsigned quals, - bool pack) + unsigned, + bool) { - ASTVisitor& V = getASTVisitor(); - if (V.checkSpecialNamespace(Result, NNS, nullptr)) - { - return; - } - if(TArgs) { auto I = std::make_unique(); @@ -70,7 +59,7 @@ buildTerminal( { I->Name = II->getName(); } - V.populate(I->TemplateArgs, *TArgs); + getASTVisitor().populate(I->TemplateArgs, *TArgs); Result = std::move(I); } else @@ -84,7 +73,7 @@ buildTerminal( } if (NNS) { - Result->Prefix = V.toNameInfo(NNS); + Result->Prefix = getASTVisitor().toNameInfo(NNS); } } @@ -94,42 +83,45 @@ buildTerminal( const NestedNameSpecifier* NNS, const NamedDecl* D, std::optional> const& TArgs, - unsigned quals, - bool pack) + unsigned, + bool) { - ASTVisitor& V = getASTVisitor(); - if (V.checkSpecialNamespace(Result, NNS, D)) + // Look for the Info type. If this is a template specialization, + // we look for the Info of the specialized record. + Decl const* ID = decayToPrimaryTemplate(D); + Info const* I = getASTVisitor().findOrTraverse(const_cast(ID)); + if (!I) { return; } - const IdentifierInfo* II = D->getIdentifier(); - if(TArgs) + auto TI = std::make_unique(); + + auto populateNameInfo = [&](NameInfo* Name, NamedDecl const* D) { - auto I = std::make_unique(); - if (II) + if(const IdentifierInfo* II = D->getIdentifier()) { - I->Name = II->getName(); + Name->Name = II->getName(); } - V.upsertDependency(getInstantiatedFrom(D), I->id); - V.populate(I->TemplateArgs, *TArgs); - Result = std::move(I); - } - else - { - auto I = std::make_unique(); - if (II) + Name->id = I->id; + if(NNS) { - I->Name = II->getName(); + Name->Prefix = getASTVisitor().toNameInfo(NNS); } - V.upsertDependency(getInstantiatedFrom(D), I->id); - Result = std::move(I); + }; + + if (!TArgs) + { + populateNameInfo(TI.get(), D); } - if (NNS) + else { - Result->Prefix = V.toNameInfo(NNS); + auto Name = std::make_unique(); + populateNameInfo(Name.get(), D); + getASTVisitor().populate(Name->TemplateArgs, *TArgs); + TI = std::move(Name); } + Result = std::move(TI); } - } // clang::mrdocs diff --git a/src/lib/AST/NameInfoBuilder.hpp b/src/lib/AST/NameInfoBuilder.hpp index f80a740623..368ffb7185 100644 --- a/src/lib/AST/NameInfoBuilder.hpp +++ b/src/lib/AST/NameInfoBuilder.hpp @@ -27,7 +27,8 @@ class NameInfoBuilder public: using TerminalTypeVisitor::TerminalTypeVisitor; - std::unique_ptr result() + std::unique_ptr + result() { return std::move(Result); } diff --git a/src/lib/AST/SymbolFilter.hpp b/src/lib/AST/SymbolFilter.hpp deleted file mode 100644 index b5ede4e113..0000000000 --- a/src/lib/AST/SymbolFilter.hpp +++ /dev/null @@ -1,93 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_LIB_AST_SYMBOLFILTER_HPP -#define MRDOCS_LIB_AST_SYMBOLFILTER_HPP - -#include "lib/Lib/Filters.hpp" - -namespace clang { -namespace mrdocs { - -/** Filter for symbols - - This class is used to filter symbols based on the - configuration provided by the user. - - */ -struct SymbolFilter -{ - const FilterNode& root; - const FilterNode* current = nullptr; - const FilterNode* last_explicit = nullptr; - bool detached = false; - - SymbolFilter(const FilterNode& root_node) - : root(root_node) - { - setCurrent(&root, false); - } - - SymbolFilter(const SymbolFilter&) = delete; - SymbolFilter(SymbolFilter&&) = delete; - - void - setCurrent( - const FilterNode* node, - bool node_detached) - { - current = node; - detached = node_detached; - if(node && node->Explicit) - last_explicit = node; - } -}; - -/** Scope for symbol filtering - - This class is used to scope the symbol filter state - during the traversal of the AST. - - It stores the state of the filter before entering - a scope and restores it when leaving the scope, after - the traversal of that scope is complete. - */ -class FilterScope -{ - SymbolFilter& filter_; - FilterNode const* current_prev_; - FilterNode const* last_explicit_prev_; - bool detached_prev_; - -public: - explicit - FilterScope(SymbolFilter& filter) - : filter_(filter) - , current_prev_(filter.current) - , last_explicit_prev_(filter.last_explicit) - , detached_prev_(filter.detached) - { - } - - ~FilterScope() - { - filter_.current = current_prev_; - filter_.last_explicit = last_explicit_prev_; - filter_.detached = detached_prev_; - } -}; - -} // mrdocs -} // clang - -#endif diff --git a/src/lib/AST/SymbolFilterScope.hpp b/src/lib/AST/SymbolFilterScope.hpp deleted file mode 100644 index fb737666c5..0000000000 --- a/src/lib/AST/SymbolFilterScope.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// -// This is a derivative work. originally part of the LLVM Project. -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_LIB_AST_SYMBOLFILTERSCOPE_HPP -#define MRDOCS_LIB_AST_SYMBOLFILTERSCOPE_HPP - -#include "lib/AST/SymbolFilter.hpp" - -namespace clang { -namespace mrdocs { - -/** Scope for symbol filtering - - This class is used to scope the symbol filter state - during the traversal of the AST. - - It stores the state of the filter before entering - a scope and restores it when leaving the scope, after - the traversal of that scope is complete. - */ -class SymbolFilterScope -{ - SymbolFilter& filter_; - FilterNode const* current_prev_; - FilterNode const* last_explicit_prev_; - bool detached_prev_; - -public: - explicit - SymbolFilterScope(SymbolFilter& filter) - : filter_(filter) - , current_prev_(filter.current) - , last_explicit_prev_(filter.last_explicit) - , detached_prev_(filter.detached) - { - } - - ~SymbolFilterScope() - { - filter_.current = current_prev_; - filter_.last_explicit = last_explicit_prev_; - filter_.detached = detached_prev_; - } -}; - -} // mrdocs -} // clang - -#endif diff --git a/src/lib/AST/TerminalTypeVisitor.hpp b/src/lib/AST/TerminalTypeVisitor.hpp index c35abf6aa1..11910eea9b 100644 --- a/src/lib/AST/TerminalTypeVisitor.hpp +++ b/src/lib/AST/TerminalTypeVisitor.hpp @@ -19,11 +19,53 @@ namespace clang::mrdocs { -/** A visitor class for terminal types. - - This class is used to visit various terminal types in the AST. - It derives from the TypeVisitor class and provides specific - implementations for visiting different types. +/** A visitor to build objects from `Type`s. + + MrDocs might need to convert class instances derived from `Type` + into other struct instances like `TypeInfo` or `NameInfo`. + + This class can be used to define a visitor to build objects + from `Type`s. The visitor can be defined as a class that + derives from `TerminalTypeVisitor`: + + @code + class SomeTypeBuilder + : public TerminalTypeVisitor { + public: + void build{DerivedType}({DerivedType}* T); + // ... + void buildTerminal(...) { ... } + // ... + void populate(const FunctionType* T); + }; + @endcode + + When `SomeTypeBuilder::Visit` is called, the `Type` is cast + to the derived class type, any corresponding information is gathered, + and `Visit()` is called again with internal types until we reach + a terminal type. When a terminal type is reached, the corresponding + `build{DerivedType}` function or a `buildTerminal` overload is called. + + This base class implements the common functionality to + visit different types and build the corresponding objects, + so that only the specific `build{DerivedType}` functions + need to be implemented by the derived class. + + It inherits the `bool Visit(const Type *T)` function + from `TerminalTypeVisitor`, which converts the + `Type` into the concrete type and calls the corresponding + `VisitXXXType` function. It also provides + `bool Visit(QualType QT)` as an extension to visit the + `Type` associated with the qualified type. + + Each `VisitXXXType` function will store any relative information + about that type and call the corresponding `VisitXXXType` function + for the internal type. For instance, `VisitUsingType` will + call `Visit(T->getUnderlyingType())`, and so on, until we + reach terminal types. + + This process will continue recursively until `VisitXXXType` + we reach a terminal type, such as `VisitPointerType`. @tparam Derived The derived class type. */ @@ -33,10 +75,16 @@ class TerminalTypeVisitor { friend class TerminalTypeVisitor::TypeVisitor; + // The ASTVisitor instance. ASTVisitor& Visitor_; + // The local qualifiers. unsigned Quals_ = 0; + + // Whether the type is a pack expansion. bool IsPack_ = false; + + // The optional NestedNameSpecifier. const NestedNameSpecifier* NNS_; public: @@ -57,6 +105,27 @@ class TerminalTypeVisitor { } + /** Visit a Type. + + This function casts the given Type to the derived class type + and calls the corresponding `VisitXXXType` function. + */ + using TerminalTypeVisitor::TypeVisitor::Visit; + + /** Visit a Qualified Type. + + This function stores the local qualifiers of the given + Qualified Type and calls the corresponding `VisitXXXType` + function for the associated `Type`. + */ + bool + Visit(QualType const QT) + { + Quals_ |= QT.getLocalFastQualifiers(); + Type const* T = QT.getTypePtrOrNull(); + return Visit(T); + } + /** Get the ASTVisitor instance. This function returns a reference to the ASTVisitor instance. @@ -64,99 +133,140 @@ class TerminalTypeVisitor @return A reference to the ASTVisitor instance. */ ASTVisitor& - getASTVisitor() + getASTVisitor() const { return Visitor_; } - using TerminalTypeVisitor::TypeVisitor::Visit; - - bool - Visit(QualType const QT) - { - Quals_ |= QT.getLocalFastQualifiers(); - return Visit(QT.getTypePtrOrNull()); - } + /** A placeholder for `buildPointer` + This function is an empty placeholder for `buildPointer` when + not defined by the `Derived` visitor. + */ void buildPointer - (const PointerType* T, - unsigned quals) + (const PointerType*, + unsigned) { } + /** A placeholder for `buildLValueRef + + This function is an empty placeholder for `buildLValueReference` when + not defined by the `Derived` visitor. + */ void buildLValueReference( - const LValueReferenceType* T) + const LValueReferenceType*) { } + /** A placeholder for `buildRValueRef + + This function is an empty placeholder for `buildRValueReference` when + not defined by the `Derived` visitor. + */ void buildRValueReference( - const RValueReferenceType* T) + const RValueReferenceType*) { } + /** A placeholder for `buildMemberPoi + + This function is an empty placeholder for `buildMemberPointer` when + not defined by the `Derived` visitor. + */ void buildMemberPointer( - const MemberPointerType* T, unsigned quals) + const MemberPointerType*, unsigned) { } + /** A placeholder for `buildArray` wh + + This function is an empty placeholder for `buildArray` when + not defined by the `Derived` visitor. + */ void buildArray( - const ArrayType* T) + const ArrayType*) { } void populate( - const FunctionType* T) + const FunctionType*) { } + /** A placeholder for `buildDecltype` + + This function is an empty placeholder for `buildDecltype` when + not defined by the `Derived` visitor. + */ void buildDecltype( - const DecltypeType* T, - unsigned quals, - bool pack) + const DecltypeType*, + unsigned, + bool) { } + /** A placeholder for `buildAuto` whe + + This function is an empty placeholder for `buildAuto` when + not defined by the `Derived` visitor. + */ void buildAuto( - const AutoType* T, - unsigned quals, - bool pack) + const AutoType*, + unsigned, + bool) { } + /** A placeholder for `buildTerminal` + + This function is an empty placeholder for `buildTerminal` when + not defined by the `Derived` visitor. + */ void buildTerminal( - const NestedNameSpecifier* NNS, - const Type* T, - unsigned quals, - bool pack) + NestedNameSpecifier const*, + Type const*, + unsigned, + bool) { } + /** A placeholder for `buildTerminal` + + This function is an empty placeholder for `buildTerminal` when + not defined by the `Derived` visitor. + */ void buildTerminal( - const NestedNameSpecifier* NNS, - const IdentifierInfo* II, - std::optional> TArgs, - unsigned quals, - bool pack) + NestedNameSpecifier const*, + IdentifierInfo const*, + std::optional>, + unsigned, + bool) { } + /** A placeholder for `buildTerminal` + + This function is an empty placeholder for `buildTerminal` when + not defined by the `Derived` visitor. + */ void buildTerminal( - const NestedNameSpecifier* NNS, - const NamedDecl* D, - std::optional> TArgs, - unsigned quals, - bool pack) + NestedNameSpecifier const*, + NamedDecl*, + std::optional>, + unsigned, + bool) { } @@ -174,56 +284,66 @@ class TerminalTypeVisitor return static_cast(*this); } + // A type with parentheses, e.g., `(int)`. bool VisitParenType( const ParenType* T) { - return Visit(T->getInnerType()); + QualType I = T->getInnerType(); + return Visit(I); } bool VisitMacroQualified( const MacroQualifiedType* T) { - return Visit(T->getUnderlyingType()); + QualType UT = T->getUnderlyingType(); + return Visit(UT); } bool VisitAttributedType( const AttributedType* T) { - return Visit(T->getModifiedType()); + QualType MT = T->getModifiedType(); + return Visit(MT); } bool VisitAdjustedType( const AdjustedType* T) { - return Visit(T->getOriginalType()); + QualType OT = T->getOriginalType(); + return Visit(OT); } bool VisitUsingType( const UsingType* T) { - return Visit(T->getUnderlyingType()); + QualType UT = T->getUnderlyingType(); + return Visit(UT); } bool VisitSubstTemplateTypeParmType( const SubstTemplateTypeParmType* T) { - return Visit(T->getReplacementType()); + QualType RT = T->getReplacementType(); + return Visit(RT); } // ---------------------------------------------------------------- + // A type that was referred to using an elaborated type keyword, + // e.g., `struct S`, or via a qualified name, e.g., `N::M::type`. bool VisitElaboratedType( const ElaboratedType* T) { NNS_ = T->getQualifier(); - return Visit(T->getNamedType()); + QualType NT = T->getNamedType(); + return Visit(NT); } bool @@ -231,7 +351,8 @@ class TerminalTypeVisitor const PackExpansionType* T) { IsPack_ = true; - return Visit(T->getPattern()); + QualType PT = T->getPattern(); + return Visit(PT); } // ---------------------------------------------------------------- @@ -241,7 +362,8 @@ class TerminalTypeVisitor const PointerType* T) { getDerived().buildPointer(T, std::exchange(Quals_, 0)); - return Visit(T->getPointeeType()); + QualType PT = T->getPointeeType(); + return Visit(PT); } bool @@ -250,7 +372,8 @@ class TerminalTypeVisitor { getDerived().buildLValueReference(T); Quals_ = 0; - return Visit(T->getPointeeType()); + QualType PT = T->getPointeeType(); + return Visit(PT); } bool @@ -259,7 +382,8 @@ class TerminalTypeVisitor { getDerived().buildRValueReference(T); Quals_ = 0; - return Visit(T->getPointeeType()); + QualType PT = T->getPointeeType(); + return Visit(PT); } bool @@ -267,7 +391,8 @@ class TerminalTypeVisitor const MemberPointerType* T) { getDerived().buildMemberPointer(T, std::exchange(Quals_, 0)); - return Visit(T->getPointeeType()); + QualType PT = T->getPointeeType(); + return Visit(PT); } bool @@ -275,7 +400,8 @@ class TerminalTypeVisitor const FunctionType* T) { getDerived().populate(T); - return Visit(T->getReturnType()); + QualType RT = T->getReturnType(); + return Visit(RT); } bool @@ -283,7 +409,8 @@ class TerminalTypeVisitor const ArrayType* T) { getDerived().buildArray(T); - return Visit(T->getElementType()); + QualType ET = T->getElementType(); + return Visit(ET); } // ---------------------------------------------------------------- @@ -312,8 +439,8 @@ class TerminalTypeVisitor const DeducedTemplateSpecializationType* T) { // KRYSTIAN TODO: we should probably add a TypeInfo - // to represent deduced types that also stores what - // it was deduced as. + // to represent deduced types also stores what it + // was deduced as. if (QualType DT = T->getDeducedType(); !DT.isNull()) { return Visit(DT); @@ -357,32 +484,45 @@ class TerminalTypeVisitor return true; } + // Visit a template specialization such as `A` bool VisitTemplateSpecializationType( - const TemplateSpecializationType* T) + TemplateSpecializationType const* T) { if (auto SFINAE = getASTVisitor().isSFINAEType(T); SFINAE.has_value()) { return getDerived().Visit(SFINAE->first); } + // In most cases, a template name is simply a reference + // to a class template. In `X xi;` the template name + // is `template class X { };`. + // Template names can also refer to function templates, + // C++0x template aliases, etc... TemplateName const TN = T->getTemplateName(); MRDOCS_ASSERT(! TN.isNull()); - NamedDecl* ND = TN.getAsTemplateDecl(); - if(! T->isTypeAlias()) + + // The list of template parameters and a reference to + // the templated scoped declaration + NamedDecl* D = TN.getAsTemplateDecl(); + + if (!T->isTypeAlias()) { auto* CT = T->getCanonicalTypeInternal().getTypePtrOrNull(); if (auto* ICT = dyn_cast_or_null(CT)) { - ND = ICT->getDecl(); + D = ICT->getDecl(); } else if (auto* RT = dyn_cast_or_null(CT)) { - ND = RT->getDecl(); + D = RT->getDecl(); } } - getDerived().buildTerminal(NNS_, ND, - T->template_arguments(), Quals_, IsPack_); + + getDerived().buildTerminal( + NNS_, D, + T->template_arguments(), + Quals_, IsPack_); return true; } @@ -468,7 +608,6 @@ class TerminalTypeVisitor NNS_, T, Quals_, IsPack_); return true; } - }; } // clang::mrdocs diff --git a/src/lib/AST/TypeInfoBuilder.cpp b/src/lib/AST/TypeInfoBuilder.cpp index b4a9030959..6341c53428 100644 --- a/src/lib/AST/TypeInfoBuilder.cpp +++ b/src/lib/AST/TypeInfoBuilder.cpp @@ -20,7 +20,7 @@ TypeInfoBuilder:: buildPointer(const PointerType* T, unsigned quals) { auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + I->CVQualifiers = toQualifierKind(quals); *std::exchange(Inner, &I->PointeeType) = std::move(I); } @@ -45,7 +45,7 @@ TypeInfoBuilder:: buildMemberPointer(const MemberPointerType* T, unsigned quals) { auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + I->CVQualifiers = toQualifierKind(quals); // do not set NNS because the parent type is *not* // a nested-name-specifier which qualifies the pointee type I->ParentType = getASTVisitor().toTypeInfo( @@ -82,9 +82,9 @@ populate(const FunctionType* T) I->ParamTypes.emplace_back( getASTVisitor().toTypeInfo(PT)); } - I->RefQualifier = convertToReferenceKind( + I->RefQualifier = toReferenceKind( FPT->getRefQualifier()); - I->CVQualifiers = convertToQualifierKind( + I->CVQualifiers = toQualifierKind( FPT->getMethodQuals().getFastQualifiers()); I->IsVariadic = FPT->isVariadic(); getASTVisitor().populate(I->ExceptionSpec, FPT); @@ -101,7 +101,7 @@ buildDecltype( auto I = std::make_unique(); getASTVisitor().populate( I->Operand, T->getUnderlyingExpr()); - I->CVQualifiers = convertToQualifierKind(quals); + I->CVQualifiers = toQualifierKind(quals); *Inner = std::move(I); Result->IsPackExpansion = pack; } @@ -114,7 +114,7 @@ buildAuto( bool const pack) { auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + I->CVQualifiers = toQualifierKind(quals); I->Keyword = convertToAutoKind(T->getKeyword()); if(T->isConstrained()) { @@ -143,19 +143,14 @@ buildTerminal( unsigned quals, bool pack) { - if(getASTVisitor().checkSpecialNamespace(*Inner, NNS, nullptr)) - { - return; - } - - auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + auto TI = std::make_unique(); + TI->CVQualifiers = toQualifierKind(quals); auto Name = std::make_unique(); Name->Name = getASTVisitor().toString(T); Name->Prefix = getASTVisitor().toNameInfo(NNS); - I->Name = std::move(Name); - *Inner = std::move(I); + TI->Name = std::move(Name); + *Inner = std::move(TI); Result->IsPackExpansion = pack; } @@ -168,14 +163,8 @@ buildTerminal( unsigned quals, bool pack) { - ASTVisitor& V = getASTVisitor(); - if(V.checkSpecialNamespace(*Inner, NNS, nullptr)) - { - return; - } - auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + I->CVQualifiers = toQualifierKind(quals); if(TArgs) { @@ -185,7 +174,7 @@ buildTerminal( Name->Name = II->getName(); } Name->Prefix = getASTVisitor().toNameInfo(NNS); - V.populate(Name->TemplateArgs, *TArgs); + getASTVisitor().populate(Name->TemplateArgs, *TArgs); I->Name = std::move(Name); } else @@ -205,52 +194,55 @@ buildTerminal( void TypeInfoBuilder:: buildTerminal( - const NestedNameSpecifier* NNS, - const NamedDecl* D, + NestedNameSpecifier const* NNS, + NamedDecl* D, std::optional> TArgs, unsigned quals, bool pack) { - ASTVisitor& V = getASTVisitor(); - if(V.checkSpecialNamespace(*Inner, NNS, D)) + MRDOCS_SYMBOL_TRACE(D, getASTVisitor().context_); + MRDOCS_SYMBOL_TRACE(NNS, getASTVisitor().context_); + + // Look for the Info type. If this is a template specialization, + // we look for the Info of the specialized record. + Decl const* ID = decayToPrimaryTemplate(D); + MRDOCS_SYMBOL_TRACE(ID, getASTVisitor().context_); + + Info const* I = getASTVisitor().findOrTraverse(const_cast(ID)); + if (!I) { return; } - auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); + auto TI = std::make_unique(); + TI->CVQualifiers = toQualifierKind(quals); - if(TArgs) + auto populateNameInfo = [&](NameInfo* Name, NamedDecl* D) { - auto Name = std::make_unique(); if(const IdentifierInfo* II = D->getIdentifier()) { Name->Name = II->getName(); } - V.upsertDependency(getInstantiatedFrom(D), Name->id); + Name->id = I->id; if(NNS) { - Name->Prefix = V.toNameInfo(NNS); + Name->Prefix = getASTVisitor().toNameInfo(NNS); } + }; - V.populate(Name->TemplateArgs, *TArgs); - I->Name = std::move(Name); + if (!TArgs) + { + TI->Name = std::make_unique(); + populateNameInfo(TI->Name.get(), D); } else { - auto Name = std::make_unique(); - if(const IdentifierInfo* II = D->getIdentifier()) - { - Name->Name = II->getName(); - } - V.upsertDependency(getInstantiatedFrom(D), Name->id); - if(NNS) - { - Name->Prefix = V.toNameInfo(NNS); - } - I->Name = std::move(Name); + auto Name = std::make_unique(); + populateNameInfo(Name.get(), D); + getASTVisitor().populate(Name->TemplateArgs, *TArgs); + TI->Name = std::move(Name); } - *Inner = std::move(I); + *Inner = std::move(TI); Result->IsPackExpansion = pack; } diff --git a/src/lib/AST/TypeInfoBuilder.hpp b/src/lib/AST/TypeInfoBuilder.hpp index 88c6acc741..4112d07673 100644 --- a/src/lib/AST/TypeInfoBuilder.hpp +++ b/src/lib/AST/TypeInfoBuilder.hpp @@ -18,79 +18,206 @@ namespace clang::mrdocs { -/** A builder class for type information. +/** A visitor to build a `mrdocs::TypeInfo` from a `clang::Type`. This class is used to build type information by visiting - various terminal types. - - It derives from the TerminalTypeVisitor class and provides - specific implementations for building different types of - type information. + various terminal types. The method `Visit` from the + base class can be used to iterate over the type information + and build the corresponding `TypeInfo` object: + + @code + TypeInfoBuilder Builder(astVisitor); + Builder.Visit(qt); + std::unique_ptr typeInfo = Builder.result(); + @endcode */ class TypeInfoBuilder : public TerminalTypeVisitor { std::unique_ptr Result; + + /* A pointer to the inner type being constructed. + + This variable is used to chain the construction of + nested type information. + + It temporarily holds the current inner type + until it is assigned to the appropriate member + of the outer type. + */ std::unique_ptr* Inner = &Result; public: using TerminalTypeVisitor::TerminalTypeVisitor; + /** Get the result of the type information. + + This function returns the result of the type information + as a unique pointer to the `TypeInfo` object. + + @return A unique pointer to the `TypeInfo` object. + */ std::unique_ptr result() { return std::move(Result); } - void - buildPointer(const PointerType* T, unsigned quals); + /** Build type information for a pointer type. - void - buildLValueReference(const LValueReferenceType* T); + Create a `PointerTypeInfo` object and populate it with + the qualifiers and the pointee type. - void - buildRValueReference(const RValueReferenceType* T); + @param T The pointer type to build. + @param quals The qualifiers for the pointer type. + */ + void buildPointer(const PointerType* T, unsigned quals); - void - buildMemberPointer(const MemberPointerType* T, unsigned quals); + /** Build type information for a lvalue reference type. - void - buildArray(const ArrayType* T); + Create a `LValueReferenceTypeInfo` object and populate it with + the pointee type. - void - populate(const FunctionType* T); + @param T The lvalue reference type to build. + */ + void buildLValueReference(const LValueReferenceType* T); - void - buildDecltype( - const DecltypeType* T, - unsigned quals, - bool pack); + /** Build type information for an rvalue reference type. + + Create a `RValueReferenceTypeInfo` object and populate it with + the pointee type. + + @param T The rvalue reference type to build. + */ + void buildRValueReference(const RValueReferenceType* T); + + /** Build type information for a member pointer type. + + Create a `MemberPointerTypeInfo` object and populate it with + the qualifiers and the parent type. + + A `MemberPointerTypeInfo` object is used to represent a pointer + to a member of a class. + + @param T The member pointer type to build. + @param quals The qualifiers for the member pointer type. + */ + void buildMemberPointer(const MemberPointerType* T, unsigned quals); + + /** Build type information for an array type. + + Create an `ArrayTypeInfo` object and populate it with the + element type and the array bounds. + + An `ArrayTypeInfo` object is used to represent an array type. + It includes the internal `TypeInfo` object for the element type + and the expression defining the array bounds. + + @param T The array type to build. + */ + void buildArray(const ArrayType* T); + + /** Populate type information for a function type. - void - buildAuto( - const AutoType* T, - unsigned const quals, - bool const pack); + Create a `FunctionTypeInfo` object and populate it with + the function type information. - void - buildTerminal( - const NestedNameSpecifier* NNS, - const Type* T, + A `FunctionTypeInfo` object is used to represent a function type. + It includes the return type and the parameter types. + + @param T The function type to populate. + */ + void populate(const FunctionType* T); + + /** Build type information for a decltype type. + + Create a `DecltypeTypeInfo` object and populate it with + the underlying expression. + + A `DecltypeTypeInfo` object is used to represent a decltype type. + It includes the underlying expression. + + @param T The decltype type to build. + @param quals The qualifiers for the decltype type. + @param pack Whether the decltype type is a pack. + */ + void buildDecltype(const DecltypeType* T, unsigned quals, bool pack); + + /** Build type information for an auto type. + + Create an `AutoTypeInfo` object and populate it with + the qualifiers. + + An `AutoTypeInfo` object is used to represent an auto type. + It includes the qualifiers for the auto type, the keyword + used to declare the auto type, and constraints. + + @param T The auto type to build. + @param quals The qualifiers for the auto type. + @param pack Whether the auto type is a pack. + */ + void buildAuto(const AutoType* T, unsigned quals, bool pack); + + /** Build type information for a terminal type. + + Create a `NamedTypeInfo` object and populate it with + the name information. + + A `NamedTypeInfo` object is used to represent a terminal type. + It includes the name information, the nested name specifier, + and the qualifiers for the terminal type. + + @param NNS The nested name specifier. + @param T The terminal type to build. + @param quals The qualifiers for the terminal type. + @param pack Whether the terminal type is a pack. + */ + void buildTerminal( + NestedNameSpecifier const* NNS, + Type const* T, unsigned quals, bool pack); - void - buildTerminal( - const NestedNameSpecifier* NNS, - const IdentifierInfo* II, + /** Build type information for a terminal type with an identifier. + + Create a `NamedTypeInfo` object and populate it with + the name information. + + A `NamedTypeInfo` object is used to represent a terminal type. + It includes the name information, the nested name specifier, + and the qualifiers for the terminal type. + + @param NNS The nested name specifier. + @param II The identifier info. + @param TArgs The template arguments. + @param quals The qualifiers for the terminal type. + @param pack Whether the terminal type is a pack. + */ + void buildTerminal( + NestedNameSpecifier const* NNS, + IdentifierInfo const* II, std::optional> TArgs, unsigned quals, bool pack); - void - buildTerminal( - const NestedNameSpecifier* NNS, - const NamedDecl* D, + /** Build type information for a terminal type with a named declaration. + + Create a `NamedTypeInfo` object and populate it with + the name information. + + A `NamedTypeInfo` object is used to represent a terminal type. + It includes the name information, the nested name specifier, + and the qualifiers for the terminal type. + + @param NNS The nested name specifier. + @param D The named declaration. + @param TArgs The template arguments. + @param quals The qualifiers for the terminal type. + @param pack Whether the terminal type is a pack. + */ + void buildTerminal( + NestedNameSpecifier const* NNS, + NamedDecl* D, std::optional> TArgs, unsigned quals, bool pack); diff --git a/src/lib/Dom/Array.cpp b/src/lib/Dom/Array.cpp index b8d9f3fb28..c2df24665a 100644 --- a/src/lib/Dom/Array.cpp +++ b/src/lib/Dom/Array.cpp @@ -150,7 +150,7 @@ type_key() const noexcept void ArrayImpl:: emplace_back( - value_type value) + value_type) { Error("Array is const").Throw(); } diff --git a/src/lib/Gen/hbs/Builder.cpp b/src/lib/Gen/hbs/Builder.cpp index 3d9f6d6c81..2966e7b4c8 100644 --- a/src/lib/Gen/hbs/Builder.cpp +++ b/src/lib/Gen/hbs/Builder.cpp @@ -343,7 +343,7 @@ operator()(std::ostream& os, T const& I) auto const wrapperFile = fmt::format("wrapper.{}.hbs", domCorpus.fileExtension); dom::Object wrapperCtx = createFrame(ctx); wrapperCtx.set("contents", dom::makeInvocable([this, &I, templateFile, &os]( - dom::Value const& options) -> Expected + dom::Value const&) -> Expected { // Helper to write contents directly to stream MRDOCS_TRY(callTemplate(os, templateFile, createContext(I))); @@ -362,7 +362,7 @@ renderWrapped( "wrapper.{}.hbs", domCorpus.fileExtension); dom::Object ctx; ctx.set("contents", dom::makeInvocable([&]( - dom::Value const& options) -> Expected + dom::Value const&) -> Expected { MRDOCS_TRY(contentsCb()); return {}; diff --git a/src/lib/Gen/hbs/HandlebarsCorpus.cpp b/src/lib/Gen/hbs/HandlebarsCorpus.cpp index 4aa93f98bf..ed8c087233 100644 --- a/src/lib/Gen/hbs/HandlebarsCorpus.cpp +++ b/src/lib/Gen/hbs/HandlebarsCorpus.cpp @@ -11,6 +11,7 @@ // #include "HandlebarsCorpus.hpp" +#include "VisitorHelpers.hpp" #include #include #include @@ -37,6 +38,22 @@ domCreate( entries.emplace_back( "description", std::move(s)); } + // Direction + if (I.direction == doc::ParamDirection::in) + { + entries.emplace_back( + "direction", "in"); + } + else if (I.direction == doc::ParamDirection::out) + { + entries.emplace_back( + "direction", "out"); + } + else if (I.direction == doc::ParamDirection::inout) + { + entries.emplace_back( + "direction", "inout"); + } return dom::Object(std::move(entries)); } @@ -98,6 +115,149 @@ domCreate( return corpus.toStringFn(corpus, I); } +Info const* +resolveTypedef(Corpus const& c, Info const& I) +{ + if (I.Kind == InfoKind::Typedef) + { + TypedefInfo const& TI = dynamic_cast(I); + std::unique_ptr const& T = TI.Type; + MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, &I); + NamedTypeInfo const& NT = dynamic_cast(*T); + MRDOCS_CHECK_OR(NT.Name, &I); + Info const* resolved = c.find(NT.Name->id); + MRDOCS_CHECK_OR(resolved, &I); + if (resolved->Kind == InfoKind::Typedef) + { + return resolveTypedef(c, *resolved); + } + return resolved; + } + return &I; +} + +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I, Info const& parent) +{ + // Look for the primary sibling in the parent scope + auto const* parentScope = dynamic_cast(&parent); + MRDOCS_CHECK_OR(parentScope, nullptr); + for (auto& siblingIDs = parentScope->Lookups.at(I.Name); + SymbolID const& siblingID: siblingIDs) + { + Info const* sibling = c.find(siblingID); + if (!sibling || + !shouldGenerate(*sibling) || + sibling->Name != I.Name) + { + continue; + } + bool const isPrimarySibling = visit(*sibling, [&](auto const& U) + { + if constexpr (requires { U.Template; }) + { + std::optional const& Template = U.Template; + MRDOCS_CHECK_OR(Template, false); + return !Template->Params.empty() && Template->Args.empty(); + } + return false; + }); + if (!isPrimarySibling) + { + continue; + } + return sibling; + } + return nullptr; +} + +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I); + +Info const* +findDirectPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ + // If the parent is a scope, look for a primary sibling + // in the parent scope for which we want to generate the URL + Info const* parent = c.find(I.Parent); + MRDOCS_CHECK_OR(parent, nullptr); + if (!shouldGenerate(*parent)) + { + parent = findPrimarySiblingWithUrl(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + return findPrimarySiblingWithUrl(c, I, *parent); +} + +Info const* +findResolvedPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ +// Check if this info is a specialization or a typedef to + // a specialization, otherwise there's nothing to resolve + bool const isSpecialization = visit(I, [&](InfoTy const& U) + { + // The symbol is a specialization + if constexpr (requires { U.Template; }) + { + std::optional const& Template = U.Template; + if (Template && + !Template->Args.empty()) + { + return true; + } + } + // The symbol is a typedef to a specialization + if constexpr (std::same_as) + { + std::unique_ptr const& T = U.Type; + MRDOCS_CHECK_OR(T && T->Kind == TypeKind::Named, false); + auto const& NT = dynamic_cast(*T); + MRDOCS_CHECK_OR(NT.Name, false); + MRDOCS_CHECK_OR(NT.Name->Kind == NameKind::Specialization, false); + return true; + } + return false; + }); + MRDOCS_CHECK_OR(isSpecialization, nullptr); + + // Find the parent scope containing the primary sibling + // for which we want to generate the URL + Info const* parent = c.find(I.Parent); + MRDOCS_CHECK_OR(parent, nullptr); + + // If the parent is a typedef, resolve it + // so we can iterate the members of this scope. + // We can't find siblings in a typedef because + // it's not a scope. + if (parent->Kind == InfoKind::Typedef) + { + parent = resolveTypedef(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + + // If the resolved parent is also a specialization or + // a dependency for which there's no URL, we attempt to + // find the primary sibling for the parent so we take + // the URL from it. + if (!shouldGenerate(*parent)) + { + parent = findPrimarySiblingWithUrl(c, *parent); + MRDOCS_CHECK_OR(parent, nullptr); + } + + return findPrimarySiblingWithUrl(c, I, *parent); +} + +Info const* +findPrimarySiblingWithUrl(Corpus const& c, Info const& I) +{ + if (Info const* primary = findDirectPrimarySiblingWithUrl(c, I)) + { + return primary; + } + return findResolvedPrimarySiblingWithUrl(c, I); +} + } // (anon) dom::Object @@ -105,8 +265,21 @@ HandlebarsCorpus:: construct(Info const& I) const { dom::Object obj = this->DomCorpus::construct(I); - obj.set("url", getURL(I)); - obj.set("anchor", names_.getQualified(I.id, '-')); + if (shouldGenerate(I)) + { + obj.set("url", getURL(I)); + obj.set("anchor", names_.getQualified(I.id, '-')); + return obj; + } + + // If the URL is not available because it's a specialization + // or dependency, we still want to generate the URL and anchor + // for the primary template if it's part of the corpus. + if (Info const* primaryInfo = findPrimarySiblingWithUrl(getCorpus(), I)) + { + obj.set("url", getURL(*primaryInfo)); + obj.set("anchor", names_.getQualified(primaryInfo->id, '-')); + } return obj; } @@ -148,7 +321,7 @@ getURL(T const& I) const } // Define Builder::operator() for each Info type -#define INFO(T) template std::string HandlebarsCorpus::getURL(T##Info const&) const; +#define INFO(T) template std::string HandlebarsCorpus::getURL(T## Info const&) const; #include template std::string HandlebarsCorpus::getURL(OverloadSet const&) const; diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.cpp b/src/lib/Gen/hbs/HandlebarsGenerator.cpp index e065b58788..093f169fc6 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.cpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.cpp @@ -118,6 +118,8 @@ build( auto errors = ex.wait(); MRDOCS_CHECK_OR(errors.empty(), Unexpected(errors)); + report::info("Generated {} pages", visitor.count()); + if (! corpus.config->tagfile.empty()) { MRDOCS_TRY(auto tagFileWriter, TagfileWriter::create( diff --git a/src/lib/Gen/hbs/MultiPageVisitor.cpp b/src/lib/Gen/hbs/MultiPageVisitor.cpp index d76ebeb2f7..8d17e73988 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.cpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.cpp @@ -10,12 +10,11 @@ // #include "MultiPageVisitor.hpp" -#include +#include "VisitorHelpers.hpp" #include +#include -namespace clang { -namespace mrdocs { -namespace hbs { +namespace clang::mrdocs::hbs { template requires std::derived_from || std::same_as @@ -23,19 +22,14 @@ void MultiPageVisitor:: operator()(T const& I0) { - /* Determine if the info is supported by this generator. - - This filters Info types for which the generator - cannot and should not generate output. - */ if constexpr (std::derived_from) { - if (I0.isSpecialization()) - { - return; - } + MRDOCS_CHECK_OR(shouldGenerate(I0)); } + // Increment the count + count_.fetch_add(1, std::memory_order_relaxed); + // If T is an OverloadSet, we make a copy for the lambda because // these are temporary objects that don't live in the corpus. // Otherwise, the lambda will capture a reference to the corpus Info. @@ -114,6 +108,4 @@ operator()(T const& I0) template void MultiPageVisitor::operator()(OverloadSet const&); -} // hbs -} // mrdocs -} // clang +} // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/MultiPageVisitor.hpp b/src/lib/Gen/hbs/MultiPageVisitor.hpp index 60239a9f50..c88566e835 100644 --- a/src/lib/Gen/hbs/MultiPageVisitor.hpp +++ b/src/lib/Gen/hbs/MultiPageVisitor.hpp @@ -19,10 +19,9 @@ #include #include #include +#include -namespace clang { -namespace mrdocs { -namespace hbs { +namespace clang::mrdocs::hbs { /** Visitor which emites a multi-page reference. */ @@ -31,6 +30,7 @@ class MultiPageVisitor ExecutorGroup& ex_; std::string_view outputPath_; Corpus const& corpus_; + std::atomic count_ = 0; public: MultiPageVisitor( @@ -51,11 +51,18 @@ class MultiPageVisitor */ template requires std::derived_from || std::same_as - void operator()(T const& I); + void + operator()(T const& I); + + /** Get number of pages generated. + */ + std::size_t + count() const noexcept + { + return count_.load(std::memory_order::relaxed); + } }; -} // hbs -} // mrdocs -} // clang +} // clang::mrdocs::hbs #endif diff --git a/src/lib/Gen/hbs/SinglePageVisitor.cpp b/src/lib/Gen/hbs/SinglePageVisitor.cpp index 6cf8ac7986..4b32246d7b 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.cpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.cpp @@ -10,12 +10,11 @@ // #include "SinglePageVisitor.hpp" +#include "VisitorHelpers.hpp" #include #include -namespace clang { -namespace mrdocs { -namespace hbs { +namespace clang::mrdocs::hbs { template requires std::derived_from || std::same_as @@ -23,17 +22,9 @@ void SinglePageVisitor:: operator()(T const& I0) { - /* Determine if the info is supported by this generator. - - This filters Info types for which the generator - cannot and should not generate output. - */ if constexpr (std::derived_from) { - if (I0.isSpecialization()) - { - return; - } + MRDOCS_CHECK_OR(shouldGenerate(I0)); } // If T is an OverloadSet, we make a copy for the lambda because @@ -73,12 +64,12 @@ operator()(T const& I0) T::isRecord() || T::isEnum()) { - corpus_.traverseOverloads(I0, *this); + corpus_.orderedTraverseOverloads(I0, *this); } } else if constexpr (std::same_as) { - corpus_.traverse(I0, *this); + corpus_.orderedTraverse(I0, *this); } } @@ -140,6 +131,4 @@ writePage( template void SinglePageVisitor::operator()(OverloadSet const&); -} // hbs -} // mrdocs -} // clang +} // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/VisitorHelpers.cpp b/src/lib/Gen/hbs/VisitorHelpers.cpp new file mode 100644 index 0000000000..ded52f0311 --- /dev/null +++ b/src/lib/Gen/hbs/VisitorHelpers.cpp @@ -0,0 +1,40 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include "VisitorHelpers.hpp" + +namespace clang::mrdocs::hbs { + +bool +shouldGenerate(Info const& I) +{ + if (I.isSpecialization()) + { + return false; + } + if (I.isEnumConstant()) + { + return false; + } + if (I.Extraction == ExtractionMode::Dependency) + { + return false; + } + if (I.Extraction == ExtractionMode::ImplementationDefined) + { + // We don't generate pages for implementation-defined symbols. + // We do generate pages for see-below symbols. + // See the requirements in ConfigOptions.json. + return false; + } + return true; +} + +} // clang::mrdocs::hbs diff --git a/src/lib/Gen/hbs/VisitorHelpers.hpp b/src/lib/Gen/hbs/VisitorHelpers.hpp new file mode 100644 index 0000000000..87fe844e6c --- /dev/null +++ b/src/lib/Gen/hbs/VisitorHelpers.hpp @@ -0,0 +1,29 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_GEN_HBS_VISITORHELPERS_HPP +#define MRDOCS_LIB_GEN_HBS_VISITORHELPERS_HPP + +#include + +namespace clang::mrdocs::hbs { + +/** @brief Determine if the generator should generate a page for this Info. + + This filters Info types for which the generator + should not generate independent pages or sections. + */ +MRDOCS_DECL +bool +shouldGenerate(Info const& I); + +} // clang::mrdocs::hbs + +#endif diff --git a/src/lib/Lib/Config.cpp b/src/lib/Lib/Config.cpp index 4cd4b4d9df..d5a37566e7 100644 --- a/src/lib/Lib/Config.cpp +++ b/src/lib/Lib/Config.cpp @@ -31,11 +31,10 @@ Expected Config::Settings:: load( Config::Settings &s, - std::string_view configYaml, + std::string_view const configYaml, ReferenceDirectories const& dirs) { MRDOCS_TRY(PublicSettings::load(s, configYaml)); - s.configDir = dirs.configDir; s.mrdocsRootDir = dirs.mrdocsRoot; s.cwdDir = dirs.cwd; s.configYaml = configYaml; @@ -56,7 +55,7 @@ load_file( { s.config = configPath; std::string configYaml = files::getFileText(s.config).value(); - Config::Settings::load(s, configYaml, dirs).value(); + MRDOCS_TRY(Config::Settings::load(s, configYaml, dirs)); return {}; } MRDOCS_CHECK(ft.value() == files::FileType::not_found, @@ -72,10 +71,9 @@ struct PublicSettingsVisitor { std::string_view name, T& value, ReferenceDirectories const& dirs, - PublicSettings::OptionProperties const& opts) - { + PublicSettings::OptionProperties const& opts) const { using DT = std::decay_t; - if constexpr (std::ranges::range) + if constexpr (std::ranges::range
) { bool const useDefault = value.empty() && std::holds_alternative
(opts.defaultValue); if (useDefault) { @@ -85,171 +83,345 @@ struct PublicSettingsVisitor { formatError("`{}` option is required", name)); if constexpr (std::same_as) { - if (!value.empty() && - (opts.type == PublicSettings::OptionType::Path || - opts.type == PublicSettings::OptionType::DirPath || - opts.type == PublicSettings::OptionType::FilePath)) + return normalizeString(self, name, value, dirs, opts, useDefault); + } + else if constexpr (std::same_as>) + { + return normalizeStringRange(self, name, value, dirs, opts, useDefault); + } + else if constexpr (std::same_as>) + { + for (auto& v : value) { - // If the path is not absolute, we need to expand it - if (!files::isAbsolute(value)) { - auto exp = getBaseDir(value, dirs, self, useDefault, opts); - if (!exp) - { - MRDOCS_TRY(value, files::makeAbsolute(value)); - } - else - { - std::string_view baseDir = *exp; - value = files::makeAbsolute(value, baseDir); - } - } - MRDOCS_CHECK(!opts.mustExist || files::exists(value), - formatError("`{}` option: path does not exist: {}", name, value)); - MRDOCS_CHECK(opts.type != PublicSettings::OptionType::DirPath || files::isDirectory(value), - formatError("`{}` option: path should be a directory: {}", name, value)); - MRDOCS_CHECK(opts.type != PublicSettings::OptionType::FilePath || !files::isDirectory(value), - formatError("`{}` option: path should be a regular file: {}", name, value)); + MRDOCS_TRY(normalizePathGlob(self, name, v, dirs, opts, useDefault)); } - else if (opts.type == PublicSettings::OptionType::String) { - if (name == "base-url") - { - if (!value.empty() && value.back() != '/') { - value.push_back('/'); - } - } + return {}; + } + } + else if constexpr (std::same_as || std::same_as) + { + return normalizeInteger(name, value, opts); + } + else + { + // Booleans and other types should already be validated because + // the struct already has their default values and there's no + // base path to prepend. + return {}; + } + return {}; + } + + Expected + normalizeString( + PublicSettings& self, + std::string_view const name, + std::string& value, + ReferenceDirectories const& dirs, + PublicSettings::OptionProperties const& opts, + bool const usingDefault) const { + if (!value.empty() + && (opts.type == PublicSettings::OptionType::Path + || opts.type == PublicSettings::OptionType::DirPath + || opts.type == PublicSettings::OptionType::FilePath)) + { + MRDOCS_TRY( + normalizeStringPath(self, name, value, dirs, opts, usingDefault)); + } + else if (opts.type == PublicSettings::OptionType::String) + { + // The base-url option should end with a slash + if (name == "base-url") + { + if (!value.empty() && value.back() != '/') + { + value.push_back('/'); } } - else if constexpr (std::same_as>) { - if (opts.type == PublicSettings::OptionType::ListPath) { - for (auto& v : value) { - if (!files::isAbsolute(v)) + } + return {}; + } + + static + Expected + normalizeStringPath( + PublicSettings& self, + std::string_view name, + std::string& value, + ReferenceDirectories const& dirs, + PublicSettings::OptionProperties const& opts, + bool const usingDefault) { + // If the path is not absolute, we need to expand it + if (!files::isAbsolute(value)) + { + // Find the base directory for this option + if (auto expBaseDir + = getBaseDir(value, dirs, self, usingDefault, opts); + !expBaseDir) + { + // Can't find the base directory, make it absolute + MRDOCS_TRY(value, files::makeAbsolute(value)); + } + else + { + std::string_view baseDir = *expBaseDir; + value = files::makeAbsolute(value, baseDir); + } + } + // Make it POSIX style + value = files::makePosixStyle(value); + if (!opts.mustExist && opts.shouldExist && !files::exists(value)) + { + report::warn( + R"("{}" option: The directory or file "{}" does not exist)", + name, + value); + } + MRDOCS_CHECK( + !opts.mustExist || files::exists(value), + formatError("`{}` option: path does not exist: {}", name, value)); + MRDOCS_CHECK( + opts.type != PublicSettings::OptionType::DirPath + || files::isDirectory(value), + formatError( + "`{}` option: path should be a directory: {}", + name, + value)); + MRDOCS_CHECK( + opts.type != PublicSettings::OptionType::FilePath + || !files::isDirectory(value), + formatError( + "`{}` option: path should be a regular file: {}", + name, + value)); + + return {}; + } + + static + Expected + normalizePathGlob( + PublicSettings& self, + std::string_view, + PathGlobPattern& value, + ReferenceDirectories const& dirs, + PublicSettings::OptionProperties const& opts, + bool const usingDefault) + { + // If the path is not absolute, we need to expand it + if (std::string_view pattern = value.pattern(); + !files::isAbsolute(pattern)) + { + // Find the base directory for this option + std::string absPattern(pattern); + if (auto expBaseDir = getBaseDir(absPattern, dirs, self, usingDefault, opts); + expBaseDir) + { + // Make the pattern absolute relative to the base directory + std::string baseDir = *expBaseDir; + baseDir = files::makePosixStyle(baseDir); + absPattern = files::makeAbsolute(pattern, baseDir); + MRDOCS_TRY(value, PathGlobPattern::create(absPattern)); + } + } + return {}; + } + + template + requires std::same_as, std::string> + Expected + normalizeStringRange( + PublicSettings& self, + std::string_view name, + T& values, + ReferenceDirectories const& dirs, + PublicSettings::OptionProperties const& opts, + bool const usingDefault) const + { + if (opts.type == PublicSettings::OptionType::ListPath) + { + MRDOCS_TRY(normalizeStringPathRange(self, name, values, dirs, opts, usingDefault)); + } + + return {}; + } + + template + Expected + normalizeStringPathRange( + PublicSettings& self, + std::string_view name, + T& values, + ReferenceDirectories const& dirs, + PublicSettings::OptionProperties const& opts, + bool const usingDefault) const + { + for (auto& value : values) + { + MRDOCS_DECL(normalizeStringPath(self, name, value, dirs, opts, usingDefault)); + } + + // Move command line sink values to appropriate destinations + if (opts.commandLineSink && opts.filenameMapping.has_value()) + { + for (auto& value : values) + { + for (auto const& map = opts.filenameMapping.value(); + auto& [from, to] : map) + { + auto filename = files::getFileName(value); + if (filename == from) + { + self.visit( + [&]( + std::string_view const otherName, U& otherValue) { - auto exp = getBaseDir(v, dirs, self, useDefault, opts); - if (!exp) - { - MRDOCS_TRY(v, files::makeAbsolute(v)); - } - else + if constexpr (std::convertible_to) { - std::string_view baseDir = *exp; - v = files::makeAbsolute(v, baseDir); - } - } - MRDOCS_CHECK(!opts.mustExist || files::exists(v), - formatError("`{}` option: path does not exist: {}", name, v)); - if (opts.commandLineSink && opts.filenameMapping.has_value()) - { - auto const& map = opts.filenameMapping.value(); - for (auto& [from, to] : map) { - auto f = files::getFileName(v); - if (f == from) + if (otherName == to) { - auto* dest = fileMapDest(self, to); - if (dest) { - *dest = v; - } + otherValue = value; } } - } + }); } } } } - else if constexpr (std::same_as || std::same_as) { - if (name == "concurrency" && std::cmp_equal(value, 0)) - { - value = std::thread::hardware_concurrency(); - } - MRDOCS_CHECK(!opts.minValue || std::cmp_greater_equal(value, *opts.minValue), - formatError("`{}` option: value {} is less than minimum: {}", name, value, *opts.minValue)); - MRDOCS_CHECK(!opts.maxValue || std::cmp_less_equal(value, *opts.maxValue), - formatError("`{}` option: value {} is greater than maximum: {}", name, value, *opts.maxValue)); - } - // Booleans should already be validated because the struct - // already has their default values return {}; } - static - std::string* - fileMapDest(PublicSettings& self, std::string_view mapDest) + template + Expected + normalizeInteger( + std::string_view name, + T& value, + PublicSettings::OptionProperties const& opts) const { - if (mapDest == "config") { - return &self.config; - } - if (mapDest == "compilationDatabase") { - return &self.compilationDatabase; + if (name == "concurrency" && std::cmp_equal(value, 0)) + { + value = std::thread::hardware_concurrency(); + return {}; } - return nullptr; + MRDOCS_CHECK( + !opts.minValue || std::cmp_greater_equal(value, *opts.minValue), + formatError( + "`{}` option: value {} is less than minimum: {}", + name, + value, + *opts.minValue)); + MRDOCS_CHECK( + !opts.maxValue || std::cmp_less_equal(value, *opts.maxValue), + formatError( + "`{}` option: value {} is greater than maximum: {}", + name, + value, + *opts.maxValue)); + return {}; } - Expected + static + Expected getBaseDir( - std::string_view referenceDirKey, + std::string_view relativeTo, ReferenceDirectories const& dirs, - PublicSettings const& settings) + PublicSettings const& self) { - if (referenceDirKey == "config-dir") { - return dirs.configDir; + if (relativeTo.empty()) + { + return Unexpected(Error("relative-to value is empty")); } - if (referenceDirKey == "cwd") { + + // Get base dir from the main reference directories + if (relativeTo == "cwd") + { return dirs.cwd; } - if (referenceDirKey == "mrdocs-root") { + if (relativeTo == "mrdocs-root") + { return dirs.mrdocsRoot; } - if (!referenceDirKey.empty()) { - Expected res = Unexpected(formatError("unknown relative-to value: \"{}\"", referenceDirKey)); - settings.visit([&](std::string_view const name, T& value) + Expected res = + Unexpected(formatError("unknown relative-to value: \"{}\"", relativeTo)); + bool found = false; + self.visit([&](std::string_view const optionName, T& value) + { + if constexpr (std::convertible_to) { - if constexpr (std::convertible_to) + if (found) + { + return; + } + if (relativeTo == optionName) { - if (name != referenceDirKey) + std::string_view valueSv(value); + if (!value.empty()) { + res = value; + found = true; return; } + res = Unexpected(formatError( + "relative-to value \"{}\" is empty", + relativeTo)); + } + else if ( + relativeTo.size() == optionName.size() + 4 && + relativeTo.starts_with(optionName) && + relativeTo.ends_with("-dir")) + { std::string_view valueSv(value); if (!value.empty()) { - res = value; + res = files::getParentDir(value); + found = true; return; } res = Unexpected(formatError( "relative-to value \"{}\" is empty", - referenceDirKey)); + relativeTo)); } - }); - return res; - } - return Unexpected(formatError("unknown relative-to value: \"{}\"", referenceDirKey)); + } + }); + return res; } static std::string_view - trimBaseDirReference(std::string_view s) + trimBaseDirReference(std::string_view const s0) { + std::string_view s = s0; if (s.size() > 2 && s.front() == '<' && - s.back() == '>') { + s.back() == '>') + { s.remove_prefix(1); s.remove_suffix(1); } return s; }; - Expected + static + Expected getBaseDir( std::string& value, ReferenceDirectories const& dirs, PublicSettings const& settings, - bool useDefault, + bool const useDefault, PublicSettings::OptionProperties const& opts) { if (!useDefault) { // If we did not use the default value, we use "relativeto" // as the base path - std::string_view relativeTo = opts.relativeto; + std::string_view relativeTo = opts.relativeTo; + if (!relativeTo.starts_with('<') || + !relativeTo.ends_with('>')) + { + return Unexpected(formatError( + "option \"{}\" has no relativeTo dir '<>'", + value)); + } relativeTo = trimBaseDirReference(relativeTo); return getBaseDir(relativeTo, dirs, settings); } @@ -257,16 +429,25 @@ struct PublicSettingsVisitor { // If we used the default value, the base dir comes from // the first path segment of the value std::string_view referenceDirKey = value; - auto pos = referenceDirKey.find('/'); + auto const pos = referenceDirKey.find('/'); if (pos != std::string::npos) { referenceDirKey = referenceDirKey.substr(0, pos); } + if (!referenceDirKey.starts_with('<') || + !referenceDirKey.ends_with('>')) + { + return Unexpected(formatError( + "default value \"{}\" has no ref dir '<>'", + value)); + } referenceDirKey = trimBaseDirReference(referenceDirKey); - MRDOCS_TRY(std::string_view baseDir, getBaseDir(referenceDirKey, dirs, settings)); + MRDOCS_TRY( + std::string_view const baseDir, + getBaseDir(referenceDirKey, dirs, settings)); if (pos != std::string::npos) { value = value.substr(pos + 1); } - return baseDir; + return std::string(baseDir); } }; @@ -277,5 +458,12 @@ normalize(ReferenceDirectories const& dirs) return PublicSettings::normalize(dirs, PublicSettingsVisitor{}); } +std::string +Config::Settings:: +configDir() const +{ + return files::getParentDir(config); +} + } // mrdocs } // clang diff --git a/src/lib/Lib/ConfigImpl.cpp b/src/lib/Lib/ConfigImpl.cpp index 41497c428e..9672096ff0 100644 --- a/src/lib/Lib/ConfigImpl.cpp +++ b/src/lib/Lib/ConfigImpl.cpp @@ -30,30 +30,6 @@ namespace mrdocs { namespace { -void -parseSymbolFilter( - FilterNode& root, - std::string_view str, - bool excluded) -{ - // FIXME: this does not handle invalid qualified-ids - std::vector parts; - if(str.starts_with("::")) - str.remove_prefix(2); - do - { - std::size_t idx = str.find("::"); - parts.emplace_back(str.substr(0, idx)); - if(idx == std::string_view::npos) - break; - str.remove_prefix(idx + 2); - } - while(! str.empty()); - // merge the parsed patterns into the filter tree - // mergeFilter(root, parts); - root.mergePattern(parts, excluded); -} - dom::Object toDomObject(std::string_view configYaml); @@ -68,14 +44,13 @@ ConfigImpl(access_token, ThreadPool& threadPool) bool ConfigImpl:: -shouldVisitSymbol( - llvm::StringRef filePath) const noexcept +shouldVisitSymbol(StringRef filePath) const noexcept { - if (settings_.input.include.empty()) + if (settings_.input.empty()) { return true; } - for (auto& p: settings_.input.include) + for (auto& p: settings_.input) { // Exact match if (filePath == p) @@ -85,10 +60,10 @@ shouldVisitSymbol( // Prefix match if (filePath.starts_with(p)) { - bool validPattern = std::ranges::any_of( - settings_.input.filePatterns, - [&](auto const &pattern) { - return globMatch(pattern, filePath); + bool const validPattern = std::ranges::any_of( + settings_.filePatterns, + [&](PathGlobPattern const &pattern) { + return pattern.match(filePath); }); if (validPattern) { @@ -102,7 +77,7 @@ shouldVisitSymbol( bool ConfigImpl:: shouldExtractFromFile( - llvm::StringRef filePath, + StringRef filePath, std::string& prefixPath) const noexcept { namespace path = llvm::sys::path; @@ -111,7 +86,7 @@ shouldExtractFromFile( if(! files::isAbsolute(filePath)) { temp = files::makePosixStyle( - files::makeAbsolute(filePath, settings_.configDir)); + files::makeAbsolute(filePath, settings_.configDir())); } else { @@ -132,37 +107,22 @@ shouldExtractFromFile( Expected> ConfigImpl:: load( - Config::Settings const& publicSettings, + Settings const& publicSettings, ReferenceDirectories const& dirs, ThreadPool& threadPool) { - std::shared_ptr c = - std::make_shared(access_token{}, threadPool); + auto c = std::make_shared(access_token{}, threadPool); MRDOCS_ASSERT(c); // Validate and copy input settings SettingsImpl& s = c->settings_; - dynamic_cast(s) = publicSettings; + dynamic_cast(s) = publicSettings; MRDOCS_TRY(Config::Settings::load(s, "", dirs)); s.configYaml = publicSettings.configYaml; // Config strings c->configObj_ = toDomObject(s.configYaml); - // Parse the filters - for(std::string_view pattern : s.filters.symbols.exclude) - parseSymbolFilter(s.symbolFilter, pattern, true); - for(std::string_view pattern : s.filters.symbols.include) - parseSymbolFilter(s.symbolFilter, pattern, false); - - // Parse the see-below and implementation-defined filters - for(std::string_view pattern: s.seeBelow) - s.seeBelowFilter.emplace_back(pattern); - for(std::string_view pattern: s.implementationDefined) - s.implementationDefinedFilter.emplace_back(pattern); - - s.symbolFilter.finalize(false, false, false); - return c; } diff --git a/src/lib/Lib/ConfigImpl.hpp b/src/lib/Lib/ConfigImpl.hpp index 4cd829f92d..9e066b50aa 100644 --- a/src/lib/Lib/ConfigImpl.hpp +++ b/src/lib/Lib/ConfigImpl.hpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -12,12 +13,9 @@ #ifndef MRDOCS_LIB_CONFIGIMPL_HPP #define MRDOCS_LIB_CONFIGIMPL_HPP -#include "lib/Lib/Filters.hpp" #include "lib/Support/YamlFwd.hpp" #include #include -#include -#include #include #include @@ -39,24 +37,7 @@ class ConfigImpl public: struct access_token {}; - struct SettingsImpl : Settings - { - /** Symbol filter root node. - - Root node of a preparsed tree of FilterNodes - used during AST traversal to determine whether - a symbol should be extracted. - */ - FilterNode symbolFilter; - - /** Namespaces for symbols rendered as "see-below". - */ - std::vector seeBelowFilter; - - /** Namespaces for symbols rendered as "implementation-defined". - */ - std::vector implementationDefinedFilter; - }; + struct SettingsImpl : Settings {}; /// @copydoc Config::settings() Settings const& @@ -147,7 +128,7 @@ class ConfigImpl If the file is visited, then prefix is set to the portion of the file path which - should be be removed for matching files. + should be removed for matching files. @param filePath A posix-style full or relative path to the file being processed. diff --git a/src/lib/Lib/ConfigOptions.json b/src/lib/Lib/ConfigOptions.json index 202c8a340d..233edfffc3 100644 --- a/src/lib/Lib/ConfigOptions.json +++ b/src/lib/Lib/ConfigOptions.json @@ -6,7 +6,7 @@ "details": "The following options can be used to control the general behavior of MrDocs and can only be provided via the command line. These include options to specify inputs and the configuration file, which cannot be set on the configuration file itself.", "options": [ { - "name": "inputs", + "name": "cmd-line-inputs", "command-line-only": true, "brief": "Configuration or compilation database files", "details": "The inputs are configuration files or compilation database files that used to generate the documentation. When the input ends with `mrdocs.yml`, it is interpreted as a configuration file, the file is read and the options are used to generate the documentation as if it was provided to the `config` option. When the input ends with `compilation_database.json` or `CMakeLists.txt`, it is interpreted as a compilation database file, the file is read and the compiler flags are used to generate the documentation as if it was provided to the `compilation-database` option.", @@ -17,7 +17,10 @@ "mrdocs.yml": "config", "compile_commands.json": "compilationDatabase", "CMakeLists.txt": "compilationDatabase" - } + }, + "relative-to": "", + "must-exist": true, + "should-exist": true }, { "name": "config", @@ -26,8 +29,9 @@ "details": "The configuration file is a YAML file that contains the options used to generate the documentation. The configuration file is read and the options are used to generate the documentation. The configuration file can be used to specify the source code, the output directory, the compilation database, the generator, and the filters.", "type": "file-path", "default": "/mrdocs.yml", - "relativeto": "", - "required": true + "relative-to": "", + "required": true, + "must-exist": true } ] }, @@ -38,9 +42,11 @@ { "name": "source-root", "brief": "Path to the root directory of the source code", + "details": "Path to the root directory of the source code. This path is used as a default for input files and a base for relative paths formed from absolute paths.", "type": "dir-path", "default": "", - "relativeto": "" + "relative-to": "", + "must-exist": true }, { "name": "output", @@ -48,8 +54,9 @@ "details": "Multipage generators expect a directory. Single page generators expect a file or a directory where the file will be created. If the directory does not exist, it will be created.", "type": "path", "default": "/reference-output", - "relativeto": "", - "must-exist": false + "relative-to": "", + "must-exist": false, + "should-exist": false }, { "name": "compilation-database", @@ -57,82 +64,144 @@ "details": "Path to the compilation database or a build script to generate it. The compilation database is a JSON file that contains the compiler commands used to build the source code. The compilation database is used to extract the compiler flags and the source files used to build the source code and extract symbols. This option also accepts the path to a build script such as CMakeLists.txt to be used to generate the compilation database. In this case, MrDocs will look for CMake in `PATH` or in `CMAKE_ROOT` and run the script to generate the compilation database file.", "type": "file-path", "default": "", - "relativeto": "" + "relative-to": "", + "must-exist": true } ] }, { - "category": "Build options", - "brief": "Options for building the source code", - "details": "When MrDocs is responsible to running the build scripts and generating the compilation database, these options are used to build the source code.", + "category": "Filters", + "brief": "Filters to include or exclude files and symbols from the documentation", "options": [ { - "name": "cmake", - "brief": "CMake arguments when generating the compilation database from CMakeLists.txt", - "details": "When the compilation-database option is a CMakeLists.txt file, these arguments are passed to the cmake command to generate the compilation_database.json.", - "type": "string", - "default": "" + "name": "input", + "brief": "Input directories to extract symbols from", + "details": "Input directories to extract. Only symbols defined in files in these directories are extracted. The paths are relative to the mrdocs configuration file.", + "type": "list", + "must-exist": false, + "should-exist": true, + "relative-to": "", + "default": [ + "/." + ] }, { - "name": "defines", - "brief": "Additional defines passed to the compiler", - "details": "Additional defines passed to the compiler when building the source code. These defines are added to the compilation database regardless of the strategy to generate it.", - "type": "list", - "default": [] + "name": "recursive", + "brief": "Recursively include files from \"input\" paths", + "details": "Recursively include files. When set to true, MrDocs includes files in subdirectories of the input directories. When set to false, MrDocs includes only the files in the input directories.", + "type": "bool", + "default": true }, { - "name": "use-system-stdlib", - "brief": "Use the system C++ standard library", - "details": "To achieve reproducible results, MrDocs bundles the LibC++ headers. To use the C++ standard library available in the system instead, set this option to true.", - "type": "bool", - "default": false + "name": "file-patterns", + "brief": "File patterns to include", + "details": "File patterns to include. Only the files that match these patterns are extracted. The patterns are relative to the input directories.", + "type": "list", + "default": [ + "*.hpp", + "*.h", + "*.hh", + "*.ipp", + "*.inc", + "*.cpp", + "*.cc", + "*.cxx", + "*.c", + "*.hxx" + ] }, { - "name": "stdlib-includes", - "brief": "C++ Standard Library include paths", - "details": "When `use-system-stdlib` is disabled, the C++ standard library headers are available in these paths.", + "name": "exclude", + "brief": "Input directories to exclude", + "details": "Symbols defined in files in these directories are not extracted even if they are in the list of include directories. When relative, the paths are relative to the directory of the mrdocs configuration file. For instance, \"include/experimental\" will exclude all files in the directory `/include/experimental`.", "type": "list", - "default": ["/share/mrdocs/headers/libcxx", "/share/mrdocs/headers/clang"], - "relativeto": "" + "must-exist": false, + "should-exist": true, + "relative-to": "", + "default": [] }, { - "name": "use-system-libc", - "brief": "Use the system C standard library", - "details": "To achieve reproducible results, MrDocs bundles the LibC headers with its definitions. To use the C standard library available in the system instead, set this option to true.", - "type": "bool", - "default": false + "name": "exclude-patterns", + "brief": "File patterns to exclude", + "details": "File patterns to exclude. Files that match these patterns are not extracted even if they are in the list of include directories. The patterns are relative to the configuration file. A single * will match all files in the directory. Double ** will match all files in the directory and its subdirectories.", + "type": "list", + "relative-to": "", + "default": [] }, { - "name": "libc-includes", - "brief": "Standard Library include paths", - "details": "When `use-system-libc` is disabled, the C standard library headers are available in these paths.", - "type": "list", - "default": ["/share/mrdocs/headers/libc-stubs"], - "relativeto": "" + "name": "include-symbols", + "brief": "Symbol patterns to include", + "details": "If any patterns are defined here, only symbols that match one of these patterns are extracted. The patterns are applied to the fully qualified name of the symbol without any leading \"::\". A single \"*\" will match all symbols in the namespace. Double \"**\" will match all symbols in the namespace and its subnamespaces. The patterns also support \"?\" for any chars, \"[]\" for charsets, \"[^]\" for inverted charsets, and \"{,...}\" for alternatives.", + "type": "list", + "relative-to": "", + "default": [] }, { - "name": "system-includes", - "brief": "System include paths", - "details": "System include paths. These paths are used to add directories to the system include search path. The system include search path is used to search for system headers. The system headers are headers that are provided by the system and are not part of the project. The system headers are used to provide the standard library headers and other system headers. The system headers are not part of the project and are not checked for warnings and errors.", - "type": "list", + "name": "exclude-symbols", + "brief": "Symbol patterns to exclude", + "details": "A symbol that matches one of these patterns is not extracted even if whitelisted by \"include-symbols\". See the documentation for \"include-symbols\" for the pattern syntax.", + "type": "list", "default": [] }, { - "name": "includes", - "brief": "Include paths", - "details": "Include paths. These paths are used to add directories to the include search path. The include search path is used to search for headers. The headers are used to provide declarations and definitions of symbols. The headers are part of the project and are checked for warnings and errors.", - "type": "list", + "name": "see-below", + "brief": "Symbols rendered as \"see-below\"", + "details": "Symbols that match one of these filters are tagged as \"see-below\" in the documentation, and so do symbols in scopes tagged as \"see-below\". This option is used to remove details about symbols that are considered part of the private API of the project. In the documentation page for this symbol, the synopsis of the implementation is rendered as \"see-below\" and members of scopes (such as a namespace or record) are not listed. The rest of the documentation is rendered as usual. See the documentation for \"include-symbol\" for the pattern syntax.", + "type": "list", + "default": [] + }, + { + "name": "implementation-defined", + "brief": "Symbols rendered as \"implementation-defined\"", + "details": "Symbols that match one of these filters are tagged as \"implementation-defined\" in the documentation, and so do symbols in scopes tagged as \"implementation-defined\". This option is used to exclude symbols from the documentation that are considered part of the private API of the project. An \"implementation-defined\" symbol has no documentation page in the output. If any other symbol refers to it, the reference is rendered as \"implementation-defined\". See the documentation for \"include-symbol\" for the pattern syntax.", + "type": "list", "default": [] } ] }, + { + "category": "Metadata Extraction", + "brief": "Metadata and C++ semantic constructs to extract", + "details": "MrDocs extracts metadata and C++ semantic constructs from the source code to create the documentation. Semantic constructs are patterns not directly represented in the source code AST but can be inferred from the corpus, such as SFINAE. The following options control the extraction of metadata and C++ semantic constructs.", + "options": [ + { + "name": "sfinae", + "brief": "Detect and reduce SFINAE expressions", + "details": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement.", + "type": "bool", + "default": true + }, + { + "name": "private-members", + "brief": "Extraction policy for private class members", + "details": "Determine whether private class members should be extracted", + "type": "bool", + "default": false + }, + { + "name": "private-bases", + "brief": "Extraction policy for private base classes", + "details": "Determine whether private base classes should be extracted", + "type": "bool", + "default": true + }, + { + "name": "anonymous-namespaces", + "brief": "Extraction policy for anonymous namespaces", + "details": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.", + "type": "bool", + "default": true + } + ] + }, { "category": "Generators", "brief": "Generators to create the documentation and their options", "options": [ { - "name": "generate", + "name": "generator", "brief": "Generator used to create the documentation", + "details": "The generator is responsible for creating the documentation from the extracted symbols. The generator uses the extracted symbols and the templates to create the documentation. The generator can create different types of documentation such as HTML, XML, and AsciiDoc.", "type": "enum", "values": [ "adoc", @@ -161,7 +230,8 @@ "details": "Path to the Addons directory. The Addons directory contains the template files used by generators to create the documentation. When a custom Addons directory is not specified, the default templates are used. The default templates are located at the `share/mrdocs/addons` directory of the MrDocs installation. Users can create custom templates by copying the default templates to a custom directory and specifying the custom directory using this option.", "type": "path", "default": "/share/mrdocs/addons", - "relativeto": "" + "relative-to": "", + "must-exist": true }, { "name": "tagfile", @@ -169,8 +239,9 @@ "details": "Specifies the full path (filename) where the generated tagfile should be saved. If left empty, no tagfile will be generated.", "type": "file-path", "default": "/reference.tag.xml", - "relativeto": "", - "must-exist": false + "relative-to": "", + "must-exist": false, + "should-exist": false }, { "name": "legible-names", @@ -181,139 +252,90 @@ }, { "name": "embedded", - "brief": "Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. This option is useful for producing documents that can be inserted into an external template.", + "brief": "Output an embeddable document", + "details": "Output an embeddable document, which excludes the header, the footer, and everything outside the body of the document. This option is useful for producing documents that can be inserted into an external template.", "type": "bool", "default": false } ] }, { - "category": "Filters", - "brief": "Filters to include or exclude files and symbols from the documentation", + "category": "Build options", + "brief": "Options for building the source code", + "details": "When MrDocs is responsible to running the build scripts and generating the compilation database, these options are used to build the source code.", "options": [ { - "name": "referenced-declarations", - "brief": "Extraction policy for references to external declarations", - "details": "Determine whether external declarations should be extracted when they are referenced in the source code. If this option is not `never`, a second pass happens in the extraction process to extract dependencies in the Corpus. When set to `always`, external declarations are always extracted. When set to `dependency`, external declarations are extracted only if they are referenced by the source code. When set to `never`, external declarations are never extracted.", - "type": "enum", - "values": [ - "always", - "dependency", - "never" - ], - "default": "dependency" + "name": "cmake", + "brief": "CMake arguments when generating the compilation database from CMakeLists.txt", + "details": "When the compilation-database option is a CMakeLists.txt file, these arguments are passed to the cmake command to generate the compilation_database.json.", + "type": "string", + "default": "" }, { - "name": "anonymous-namespaces", - "brief": "Extraction policy for anonymous namespaces", - "details": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.", - "type": "enum", - "values": [ - "always", - "dependency", - "never" - ], - "default": "always" + "name": "defines", + "brief": "Additional defines passed to the compiler", + "details": "Additional defines passed to the compiler when building the source code. These defines are added to the compilation database regardless of the strategy to generate it.", + "type": "list", + "default": [] }, { - "name": "inaccessible-members", - "brief": "Extraction policy for inaccessible members", - "details": "Determine whether inaccessible members, such as private members of records, should be extracted. When set to `always`, inaccessible members are always extracted. When set to `dependency`, inaccessible members are extracted only if they are referenced by the source code. When set to `never`, inaccessible members are never extracted.", - "type": "enum", - "values": [ - "always", - "dependency", - "never" - ], - "default": "always" + "name": "use-system-stdlib", + "brief": "Use the system C++ standard library", + "details": "To achieve reproducible results, MrDocs bundles the LibC++ headers. To use the C++ standard library available in the system instead, set this option to true.", + "type": "bool", + "default": false }, { - "name": "inaccessible-bases", - "brief": "Extraction policy for inaccessible base classes", - "details": "Determine whether inaccessible base classes should be extracted. When set to `always`, inaccessible base classes are always extracted. When set to `dependency`, inaccessible base classes are extracted only if they are referenced by the source code. When set to `never`, inaccessible base classes are never extracted.", - "type": "enum", - "values": [ - "always", - "dependency", - "never" + "name": "stdlib-includes", + "brief": "C++ Standard Library include paths", + "details": "When `use-system-stdlib` is disabled, the C++ standard library headers are available in these paths.", + "type": "list", + "default": [ + "/share/mrdocs/headers/libcxx", + "/share/mrdocs/headers/clang" ], - "default": "always" + "relative-to": "", + "must-exist": false, + "should-exist": true }, { - "name": "see-below", - "brief": "Namespaces for symbols rendered as \"see-below\"", - "details": "Namespaces for symbols rendered as \"see-below\". Symbols in these namespaces are rendered as \"see-below\" in the documentation. This option is used to exclude details about symbols from the documentation that are considered part of the private API of the project. In the documentation page for this symbol, the synopsis of the implementation of a \"see-below\" symbol is rendered as \"see-below\". When the symbol is a scope (such as a namespace or record), its members are not listed. The rest of the documentation is rendered as usual.", - "type": "list", - "default": [] + "name": "use-system-libc", + "brief": "Use the system C standard library", + "details": "To achieve reproducible results, MrDocs bundles the LibC headers with its definitions. To use the C standard library available in the system instead, set this option to true.", + "type": "bool", + "default": false }, { - "name": "implementation-defined", - "brief": "Namespaces for symbols rendered as \"implementation-defined\"", - "details": "Namespaces for symbols rendered as \"implementation-defined\". Symbols in these are rendered as \"implementation-defined\" in the documentation. This option is used to exclude symbols from the documentation that are considered part of the private API of the project. An \"implementation-defined\" symbol has no documentation page in the output. If any other symbol refers to it, the reference is rendered as \"implementation-defined\".", - "type": "list", - "default": [] + "name": "libc-includes", + "brief": "Standard Library include paths", + "details": "When `use-system-libc` is disabled, the C standard library headers are available in these paths.", + "type": "list", + "default": [ + "/share/mrdocs/headers/libc-stubs" + ], + "relative-to": "", + "must-exist": false, + "should-exist": true }, { - "name": "input", - "brief": "Include files to extract", - "details": "Include files to extract. Only the files listed in this option are extracted. The paths are relative to the mrdocs configuration file.", - "options": [ - { - "name": "include", - "brief": "Input directories to include", - "details": "Input directories to include. Only the files in these directories are extracted. The paths are relative to the mrdocs configuration file.", - "type": "list", - "must-exist": false, - "default": [] - }, - { - "name": "file-patterns", - "brief": "File patterns to include", - "details": "File patterns to include. Only the files that match these patterns are extracted. The patterns are relative to the input directories.", - "type": "list", - "default": [] - } - ] + "name": "system-includes", + "brief": "System include paths", + "details": "System include paths. These paths are used to add directories to the system include search path. The system include search path is used to search for system headers. The system headers are headers that are provided by the system and are not part of the project. The system headers are used to provide the standard library headers and other system headers. The system headers are not part of the project and are not checked for warnings and errors.", + "type": "list", + "relative-to": "", + "default": [], + "must-exist": false, + "should-exist": true }, { - "name": "filters", - "brief": "Filters", - "options": [ - { - "name": "symbols", - "brief": "Symbol filters", - "details": "Symbol filters. Symbols that match these filters are extracted. The filters are applied to the fully qualified name of the symbol.", - "options": [ - { - "name": "include", - "brief": "Specifies symbol inclusion patterns", - "details": "Specifies symbol inclusion patterns. Symbols that match these patterns are extracted. The patterns are applied to the fully qualified name of the symbol.", - "type": "list", - "default": [] - }, - { - "name": "exclude", - "brief": "Specifies symbol exclusion patterns", - "details": "Specifies symbol exclusion patterns. Symbols that match these patterns are not extracted. The patterns are applied to the fully qualified name of the symbol.", - "type": "list", - "default": [] - } - ] - } - ] - } - ] - }, - { - "category": "Metadata", - "brief": "Metadata and C++ constructs to extract", - "options": [ - { - "name": "detect-sfinae", - "brief": "Detect SFINAE expressions", - "details": "When set to true, MrDocs detects SFINAE expressions in the source code and extracts them as part of the documentation. Expressions such as `std::enable_if<...>` are detected, removed, and documented as a requirement.", - "type": "bool", - "default": true + "name": "includes", + "brief": "Include paths", + "details": "Include paths. These paths are used to add directories to the include search path. The include search path is used to search for headers. The headers are used to provide declarations and definitions of symbols. The headers are part of the project and are checked for warnings and errors.", + "type": "list", + "default": [], + "relative-to": "", + "must-exist": false, + "should-exist": true } ] }, diff --git a/src/lib/Lib/CorpusImpl.cpp b/src/lib/Lib/CorpusImpl.cpp index ed7724c06e..375a7d06df 100644 --- a/src/lib/Lib/CorpusImpl.cpp +++ b/src/lib/Lib/CorpusImpl.cpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -18,7 +19,7 @@ #include "lib/Support/Chrono.hpp" #include #include -#include +#include #include namespace clang { diff --git a/src/lib/Lib/Filters.cpp b/src/lib/Lib/Filters.cpp deleted file mode 100644 index 60c66281ec..0000000000 --- a/src/lib/Lib/Filters.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#include "Filters.hpp" - -namespace clang { -namespace mrdocs { - -FilterPattern:: -FilterPattern() -{ -#ifndef NDEBUG - // pattern for VS debugger visualizers - pattern_.push_back('*'); -#endif -} - -FilterPattern:: -FilterPattern( - std::string_view pattern) -{ - while(! pattern.empty()) - { - bool wildcard = pattern.front() == '*'; - std::size_t part_size = std::min( - pattern.size(), wildcard ? - pattern.find_first_not_of('*') : - pattern.find('*')); - - if(! wildcard) - raw_.append(pattern, 0, part_size); - - #ifndef NDEBUG - // pattern for VS debugger visualizers - pattern_.append(wildcard ? - "*" : pattern.substr(0, part_size)); - #endif - - pattern.remove_prefix(part_size); - - // don't store the parts for patterns without - // wildcards, as well as wildcard only patterns - if(pattern.empty() && parts_.empty()) - break; - - parts_.push_back(wildcard ? 0 : part_size); - } -} - -bool -FilterPattern:: -matchesSlow( - std::string_view str, - std::string_view pattern, - std::span parts) const -{ - MRDOCS_ASSERT(! parts.empty()); - const auto& part = pattern.substr(0, parts[0]); - pattern.remove_prefix(parts[0]); - parts = parts.subspan(1); - // current part is not a wild card - if(! part.empty()) - { - // every character in the pattern must - // match the beginning of the string - if(! str.starts_with(part)) - return false; - str.remove_prefix(part.size()); - // if there are no other parts, then we only - // have a match if there are no characters left - if(parts.empty()) - return str.empty(); - // if we had a match and the pattern ends in - // a wildcard, the entire string matches - if(parts.size() == 1) - return true; - return matchesSlow(str, pattern, parts); - } - // current part is a wildcard. - // check all possible lengths for this wildcard until we - // either find a match, or we run out of characters. - for(; ! str.empty(); str.remove_prefix(1)) - { - if(matchesSlow(str, pattern, parts)) - return true; - } - return false; -} - -bool -FilterPattern:: -matches(std::string_view str) const -{ - if(parts_.empty()) - { - // if the raw pattern is empty, the pattern is '*' - // and matches everything - if(raw_.empty()) - return true; - // the pattern contains no wildcards, compare with - // the raw pattern - return str == raw_; - } - // no match when the string is shorter than - // the shortest possible match size - if(str.size() < raw_.size()) - return false; - // otherwise, use the wildcard matching algorithm - return matchesSlow(str, raw_, parts_); -} - -bool -FilterPattern:: -subsumes(const FilterPattern& other) const -{ - return matches(other.raw_); -} - -const FilterNode* -FilterNode:: -findChild( - std::string_view name) const -{ - const FilterNode* best = nullptr; - for(auto& child : Children) - { - if(! child.Pattern.matches(name)) - continue; - if(! best || best->Pattern.subsumes(child.Pattern)) - best = &child; - } - return best; -} - -void -FilterNode:: -mergePattern( - std::span parts, - bool excluded) -{ - if(parts.empty()) - return; - - const auto& pattern = parts[0]; - parts = parts.subspan(1); - - std::vector subsumed; - FilterNode* matching_node = nullptr; - for(FilterNode& child : Children) - { - // if the new pattern would match everything - // that the child node would, merge the subsequent - // patterns into the child node - if(pattern.subsumes(child.Pattern)) - child.mergePattern(parts, excluded); - - // found_exact |= child.Pattern == pattern; - if(child.Pattern == pattern) - matching_node = &child; - // if an exact match has not been found, collect the - // existing children which would match this pattern - if(! matching_node && child.Pattern.subsumes(pattern)) - { - subsumed.insert(subsumed.end(), - child.Children.begin(), - child.Children.end()); - } - } - // if we didn't find an exact match, add a new node - if(! matching_node) - { - matching_node = &Children.emplace_back( - pattern, std::move(subsumed), excluded); - matching_node->mergePattern(parts, excluded); - } - - if(parts.empty()) - { - // mark terminal nodes are explicitly specified - matching_node->Explicit = true; - // whitelist overrides blacklist - matching_node->Excluded &= excluded; - } -} - -void -FilterNode:: -finalize( - bool any_parent_explicit, - bool any_parent_excluded, - bool any_parent_included) -{ - any_parent_explicit |= Explicit; - any_parent_excluded |= (Excluded && Explicit); - any_parent_included |= (! Excluded && Explicit); - - for(FilterNode& child : Children) - child.finalize( - any_parent_explicit, - any_parent_excluded, - any_parent_included); - - if(! any_parent_explicit) - return; - - std::erase_if(Children, [&](const auto& child) -> bool - { - // do not prune child nodes which are non-terminal - if(! child.Children.empty()) - return false; - if(! (any_parent_excluded || child.Excluded)) - return true; - if(! (any_parent_included || ! child.Excluded)) - return true; - return false; - }); -} - -} // mrdocs -} // clang diff --git a/src/lib/Lib/Filters.hpp b/src/lib/Lib/Filters.hpp deleted file mode 100644 index aa4fc5dc57..0000000000 --- a/src/lib/Lib/Filters.hpp +++ /dev/null @@ -1,143 +0,0 @@ -// -// Licensed under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) -// -// Official repository: https://github.com/cppalliance/mrdocs -// - -#ifndef MRDOCS_LIB_FILTERS_HPP -#define MRDOCS_LIB_FILTERS_HPP - -#include -#include -#include -#include -#include - -namespace clang { -namespace mrdocs { - -class FilterPattern -{ - // pattern without any wildcards - std::string raw_; - // pattern part lengths, where zero represents a wildcard - std::vector parts_; - -#ifndef NDEBUG - // pattern string for VS debugger visualizers - std::string pattern_; -#endif - - /** Returns whether a string matches the multi-component pattern. - - This function implements matching for patterns which contain - multiple components (i.e. both literal strings and wildcards). - */ - bool - matchesSlow( - std::string_view str, - std::string_view pattern, - std::span parts) const; - -public: - FilterPattern(); - FilterPattern(std::string_view pattern); - - bool - operator==(const FilterPattern&) const noexcept = default; - - /** Returns whether a string matches the pattern. - */ - bool - matches(std::string_view str) const; - - /** Returns whether this pattern subsumes the other. - - A pattern `a` subsumes a pattern `b` if `a` would - at least match every string that `b` does. - */ - bool - subsumes(const FilterPattern& other) const; -}; - -struct FilterNode -{ - /** The filter pattern. - - The pattern defines which symbols match this node. - */ - FilterPattern Pattern; - - /** Filter nodes for members of matching symbols. - - Members of symbols which match this node will be - matched against the child nodes. - */ - std::vector Children; - - /** Whether the node is excluded (i.e. blacklisted). - */ - bool Excluded : 1 = false; - - /** Whether the node is explicit. - - A node is explicit if it represents the last component - of a filter config string, e.g. `B` in `A::B`. - */ - bool Explicit : 1 = false; - - FilterNode() = default; - - FilterNode( - const FilterPattern& pattern, - std::vector&& children, - bool excluded) - : Pattern(pattern) - , Children(std::move(children)) - , Excluded(excluded) - { - } - - bool isTerminal() const noexcept - { - return Children.empty(); - } - - /** Find a matching child node. - - Finds the most constrained child node which - matches `name`. - */ - const FilterNode* - findChild(std::string_view name) const; - - /** Add child node for the specified pattern. - - Creates a child node from `parts` and merges - it into the children of this node. - */ - void - mergePattern( - std::span parts, - bool excluded); - - /** Prune child nodes. - - Removes any children which specify - meaningless or redundant filters. - */ - void - finalize( - bool any_parent_explicit, - bool any_parent_excluded, - bool any_parent_included); -}; - -} // mrdocs -} // clang - -#endif diff --git a/src/lib/Metadata/Info.cpp b/src/lib/Metadata/Info.cpp index 30e7de6979..75b15b7bc5 100644 --- a/src/lib/Metadata/Info.cpp +++ b/src/lib/Metadata/Info.cpp @@ -71,7 +71,11 @@ tag_invoke( } io.map("kind", I.Kind); io.map("access", I.Access); - io.map("implicit", I.Implicit); + io.map("extraction", I.Extraction); + io.map("isRegular", I.Extraction == ExtractionMode::Regular); + io.map("isSeeBelow", I.Extraction == ExtractionMode::SeeBelow); + io.map("isImplementationDefined", I.Extraction == ExtractionMode::ImplementationDefined); + io.map("isDependency", I.Extraction == ExtractionMode::Dependency); if (I.Parent) { io.map("parent", I.Parent); @@ -175,6 +179,7 @@ tag_invoke( { io.map("requires", I.Requires.Written); } + io.map("attributes", dom::LazyArray(I.Attributes)); } if constexpr (T::isTypedef()) { @@ -218,6 +223,7 @@ tag_invoke( { io.map("bitfieldWidth", I.BitfieldWidth.Written); } + io.map("attributes", dom::LazyArray(I.Attributes)); } if constexpr (T::isSpecialization()) {} @@ -287,6 +293,70 @@ tag_invoke( }); } +bool +operator<( + Info const& lhs, + Info const& rhs) noexcept +{ + if (lhs.Kind != rhs.Kind) + { + return lhs.Kind < rhs.Kind; + } + if (lhs.Name != rhs.Name) + { + return lhs.Name < rhs.Name; + } + + // If kind and name are the same, compare by template arguments + TemplateInfo const* lhsTemplate = visit(lhs, [](auto const& U) + -> TemplateInfo const* + { + if constexpr (requires { U.Template; }) + { + if (U.Template) + { + return &*U.Template; + } + } + return nullptr; + }); + TemplateInfo const* rhsTemplate = visit(rhs, [](auto const& U) + -> TemplateInfo const* + { + if constexpr (requires { U.Template; }) + { + if (U.Template) + { + return &*U.Template; + } + } + return nullptr; + }); + if (!lhsTemplate && !rhsTemplate) + { + return false; + } + if (!lhsTemplate) + { + return true; + } + if (!rhsTemplate) + { + return false; + } + if (lhsTemplate->Args.size() != rhsTemplate->Args.size()) + { + return lhsTemplate->Args.size() < rhsTemplate->Args.size(); + } + for (std::size_t i = 0; i < lhsTemplate->Args.size(); ++i) + { + if (lhsTemplate->Args[i] != rhsTemplate->Args[i]) + { + return lhsTemplate->Args[i]->Kind < rhsTemplate->Args[i]->Kind; + } + } + return false; +} } // mrdocs } // clang diff --git a/src/lib/Metadata/Interface.cpp b/src/lib/Metadata/Interface.cpp index 2752a0f2a4..62ff8387cd 100644 --- a/src/lib/Metadata/Interface.cpp +++ b/src/lib/Metadata/Interface.cpp @@ -145,8 +145,7 @@ class TrancheBuilder { auto& config = static_cast< ConfigImpl const&>(corpus_.config); - includePrivate_ = config->inaccessibleMembers != - ConfigImpl::SettingsImpl::ExtractPolicy::Never; + includePrivate_ = config->privateMembers; } void @@ -204,8 +203,8 @@ class TrancheBuilder } void operator()( - const SpecializationInfo& I, - AccessKind access) + const SpecializationInfo&, + AccessKind) { // KRYSTIAN FIXME: currently unimplemented } @@ -283,8 +282,8 @@ class TrancheBuilder } void operator()( - const EnumConstantInfo& I, - AccessKind access) + const EnumConstantInfo&, + AccessKind) { // KRYSTIAN FIXME: currently unimplemented } diff --git a/src/lib/Metadata/Name.cpp b/src/lib/Metadata/Name.cpp index 9e2677a053..c883795139 100644 --- a/src/lib/Metadata/Name.cpp +++ b/src/lib/Metadata/Name.cpp @@ -4,6 +4,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Krystian Stasiowski (sdkrystian@gmail.com) +// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // diff --git a/src/lib/Metadata/Overloads.cpp b/src/lib/Metadata/Overloads.cpp index 45e2abe689..c04e6bb19c 100644 --- a/src/lib/Metadata/Overloads.cpp +++ b/src/lib/Metadata/Overloads.cpp @@ -39,13 +39,21 @@ tag_invoke( res.set("name", overloads.Name); res.set("members", dom::LazyArray(overloads.Members, domCorpus)); - // Copy redundant fields from the first member + // Copy other redundant fields from the first member if (overloads.Members.size() > 0) { dom::Value const member = domCorpus->get(overloads.Members[0]); if (member.isObject()) { - for (std::string_view const key: {"parent", "parents"}) + for (std::string_view const key: + { "parent", + "parents", + "access", + "extraction", + "isRegular", + "isSeeBelow", + "isImplementationDefined", + "isDependency" }) { res.set(key, member.get(key)); } diff --git a/src/lib/Metadata/Reduce.cpp b/src/lib/Metadata/Reduce.cpp index 4cc0065fcb..c8a1240767 100644 --- a/src/lib/Metadata/Reduce.cpp +++ b/src/lib/Metadata/Reduce.cpp @@ -99,7 +99,8 @@ static void merge(Javadoc& I, Javadoc&& other) } } -void mergeInfo(Info& I, Info&& Other) +void +mergeInfo(Info& I, Info&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); MRDOCS_ASSERT(I.id); @@ -115,7 +116,7 @@ void mergeInfo(Info& I, Info&& Other) { I.Access = Other.Access; } - I.Implicit &= Other.Implicit; + I.Extraction = leastSpecific(I.Extraction, Other.Extraction); // Append javadocs if (!I.javadoc) diff --git a/src/lib/Metadata/Scope.cpp b/src/lib/Metadata/Scope.cpp index 4bec3ddb2a..4a24050cb4 100644 --- a/src/lib/Metadata/Scope.cpp +++ b/src/lib/Metadata/Scope.cpp @@ -44,5 +44,27 @@ generateScopeOverloadsArray( } +void +addMember( + ScopeInfo& P, + Info& C) +{ + // Include C.id in P.Members if it's not already there + if (bool const exists = std::ranges::find(P.Members, C.id) + != P.Members.end(); + !exists) + { + P.Members.emplace_back(C.id); + } + + // Include C.id in P.Lookups[C.Name] if it's not already there + auto& lookups = P.Lookups.try_emplace(C.Name).first->second; + if (bool const exists = std::ranges::find(lookups, C.id) != lookups.end(); + !exists) + { + lookups.emplace_back(C.id); + } +} + } // mrdocs } // clang diff --git a/src/lib/Support/Generator.cpp b/src/lib/Support/Generator.cpp index 6f932c5012..c496e1843e 100644 --- a/src/lib/Support/Generator.cpp +++ b/src/lib/Support/Generator.cpp @@ -47,11 +47,11 @@ Generator:: build(Corpus const& corpus) const { using clock_type = std::chrono::steady_clock; - auto start_time = clock_type::now(); - std::string absOutput = files::normalizePath( + auto const start_time = clock_type::now(); + std::string const absOutput = files::normalizePath( files::makeAbsolute( corpus.config->output, - corpus.config->configDir)); + corpus.config->configDir())); MRDOCS_TRY(build(absOutput, corpus)); report::info( "Generated {} documentation in {}", diff --git a/src/lib/Support/Glob.cpp b/src/lib/Support/Glob.cpp index 326f178dd2..52d06a216e 100644 --- a/src/lib/Support/Glob.cpp +++ b/src/lib/Support/Glob.cpp @@ -8,45 +8,620 @@ // Official repository: https://github.com/cppalliance/mrdocs // -#include "Glob.hpp" +#include +#include +#include +#include +#include +#include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { -bool -globMatch( - std::string_view pattern, - std::string_view str) noexcept +namespace { + +/* Expand character ranges in S and return a bitmap. + + For example, "a-cf-hz" is expanded into a bitmap + representing "abcfghz". + + This function is a modified version of the expand function from + llvm/lib/Support/GlobPattern.cpp. + + @param str The string to expand. + @param original The original string to use in error messages. + */ +Expected +parseCharRange( + std::string_view str, + std::string_view original) { - if (pattern.empty()) + llvm::BitVector BV(256, false); + + // Expand X-Y. + while (str.size() >= 3) { - return str.empty(); + std::uint8_t const charRangeStart = str[0]; + std::uint8_t const charRangeEnd = str[2]; + + if (str[1] != '-') + { + // Not "X-Y": consume the first character + BV[charRangeStart] = true; + str = str.substr(1); + continue; + } + + if (charRangeStart > charRangeEnd) + { + // Invalid range. + return Unexpected(formatError( + "invalid glob pattern: {}", original)); + } + + for (int c = charRangeStart; c <= charRangeEnd; ++c) + { + BV[static_cast(c)] = true; + } + str = str.substr(3); + } + + for (char const c: str) + { + BV[static_cast(c)] = true; + } + return BV; +} + +/* Expand brace expansions in a string and return a list of patterns. + + For example, "a{b,c}d" is expanded into "abd" and "acd". + + This function is a modified version of the parseBraceExpansions function from + llvm/lib/Support/GlobPattern.cpp. + + @param str The string to expand. + @param max The maximum number of subpatterns to generate. + */ +Expected> +parseBraceExpansions( + std::string_view str, + std::optional const max) +{ + // If there are no brace expansions, return the original string + // as the only subpattern. + llvm::SmallVector SubPatterns = {std::string(str)}; + + // Parse brace expansions. + struct BraceExpansion + { + // Start of the brace expansion. + std::size_t start{0}; + // Length of the brace expansion. + std::size_t length{0}; + // Terms in the brace expansion, such as "a,b,c". + llvm::SmallVector terms; + }; + llvm::SmallVector expansions; + + // The current brace expansion being parsed and populated + BraceExpansion *curBraces = nullptr; + // The start of the contents of the current brace expansion + std::size_t curBracesBegin{0}; + for (size_t i = 0, end = str.size(); i != end; ++i) + { + char const c = str[i]; + + // Skip character ranges. + if (c == '[') + { + i = str.find(']', i + 2); + if (i == std::string::npos) + { + return Unexpected(Error("invalid glob pattern, unmatched '['")); + } + continue; + } + + // Parse brace expansions + if (c == '{') + { + if (curBraces) + { + return Unexpected(Error("nested brace expansions are not supported")); + } + // Start a new brace expansion. + expansions.emplace_back(); + curBraces = &expansions.back(); + curBraces->start = i; + curBracesBegin = i + 1; + continue; + } + + // If we are not in a brace expansion, continue. + // Otherwise, parse the brace expansion. + if (c == ',') + { + if (!curBraces) + { + continue; + } + // Add a term to the current brace expansion. + std::string_view const term = str.substr( + curBracesBegin, i - curBracesBegin); + curBraces->terms.push_back(term); + curBracesBegin = i + 1; + continue; + } + + // If we are not in a brace expansion, continue. + // Otherwise, finish the brace expansion. + if (c == '}') + { + if (!curBraces) + { + continue; + } + if (curBraces->terms.empty()) + { + if (i == curBracesBegin) + { + return Unexpected(Error("empty brace expansions are not supported")); + } + return Unexpected(Error("singleton brace expansions are not supported")); + } + // Add the last term to the current brace expansion. + std::string_view const term = str.substr( + curBracesBegin, i - curBracesBegin); + curBraces->terms.push_back(term); + curBraces->length = i - curBraces->start + 1; + // Reset the current brace expansion. + curBraces = nullptr; + continue; + } + + // Skip escaped characters. + if (c == '\\') + { + if (++i == end) + { + return Unexpected(Error("invalid glob pattern, stray '\\'")); + } + } + } + + // Check for incomplete brace expansions. + if (curBraces) + { + return Unexpected(Error("incomplete brace expansion")); + } + + // Check number of subpatterns. + if (max) + { + std::size_t nSubPatterns = 1; + for (auto& expansion: expansions) + { + if (nSubPatterns > std::numeric_limits::max() / expansion.terms.size()) + { + nSubPatterns = std::numeric_limits::max(); + break; + } + nSubPatterns *= expansion.terms.size(); + } + if (max && nSubPatterns > *max) + { + return Unexpected(Error("too many brace expansions")); + } + } + + // Replace brace expansions in reverse order so that we don't invalidate + // earlier start indices + for (auto& [start, length, terms]: std::ranges::views::reverse(expansions)) + { + llvm::SmallVector OrigSubPatterns; + std::swap(SubPatterns, OrigSubPatterns); + for (std::string_view Term: terms) + { + for (std::string_view Orig: OrigSubPatterns) + { + SubPatterns.emplace_back(Orig) + .replace(start, length, Term); + } + } + } + return std::move(SubPatterns); +} + +/* A glob pattern without any {} + */ +struct SubGlobPattern { + struct CharBracket { + std::size_t next_offset_; + llvm::BitVector bytes_; + + bool + match(std::uint8_t const c) const + { + return bytes_[c]; + } + + bool + match(char const c) const + { + return bytes_[static_cast(c)]; + } + }; + + enum class MatchType { + FULL, + PARTIAL, + MISMATCH + }; + + static + Expected + create(std::string_view pattern); + + MatchType + match(std::string_view str, char delimiter) const; + + std::string_view + pattern() const { + return pattern_; } - if (pattern[0] == '*') + + // The original glob pattern. + std::string pattern_; + + // The expanded char brackets. + std::vector brackets_; +}; + +/* Create a new SubGlobPattern from a glob pattern. + + This function takes a glob pattern and creates a new SubGlobPattern + from it. The glob pattern is parsed and expanded into a list of + subpatterns that are used for matching. + + This function is a modified version of the create function from + llvm/lib/Support/GlobPattern.cpp. + + @param pattern The glob pattern to use for matching. + */ +Expected +SubGlobPattern:: +create(std::string_view pattern) +{ + SubGlobPattern res; + + // Parse brackets. + res.pattern_.assign(pattern.begin(), pattern.end()); + for (std::size_t i = 0, end = pattern.size(); i != end; ++i) { - if (pattern.size() == 1) + if (pattern[i] == '[') + { + // Check for a character range. + ++i; + // `]` is allowed as the first character of a character class. + // `[]` is invalid. + // So, just skip the first character. + std::size_t const charRangeEnd = pattern.find(']', i + 1); + if (charRangeEnd == std::string_view::npos) + { + if (pattern[i] == ']') + { + return Unexpected(Error("invalid glob pattern, empty character range")); + } + return Unexpected(Error("invalid glob pattern, unmatched '['")); + } + std::string_view rangeChars = pattern.substr(i, charRangeEnd - i); + bool const invert = pattern[i] == '^' || pattern[i] == '!'; + rangeChars = rangeChars.substr(invert); + MRDOCS_TRY(llvm::BitVector BV, parseCharRange(rangeChars, pattern)); + if (invert) + { + BV.flip(); + } + CharBracket bracket{ charRangeEnd + 1, std::move(BV) }; + res.brackets_.push_back(std::move(bracket)); + i = charRangeEnd; + } + else if (pattern[i] == '\\') + { + if (++i == end) + { + return Unexpected(Error("invalid glob pattern, stray '\\'")); + } + } + } + return res; +} + +/* Match a string against the subglob pattern. + + This function matches a string against the subglob pattern. The string is + matched against each segment of the pattern. If a segment is matched, the + next segment is matched against the remaining string. If a segment is not + matched, the function backtracks to the last '*' and tries to match the + remaining string against the next segment. + + This function is a modified version of the match function from + llvm/lib/Support/GlobPattern.cpp. + + @param str The string to match against the pattern. + */ +SubGlobPattern::MatchType +SubGlobPattern:: +match(std::string_view str, char const delimiter) const +{ + // The current position in the pattern we're matching. + std::string_view pattern = pattern_; + + // The index of the current parsed brackets we're using. + std::size_t bracketsIdx = 0; + + // The string we're matching "*". + std::string_view starStr = str; + + // The pattern suffix after the "*" run. + std::optional patternStarSuffix; + + // The saved brackets index for backtracking. + std::size_t bracketsStarMatchIdx = 0; + + // Whether the current "*" run is a double star. + bool isDoubleStar = false; + + // Attempt to match a single character in the string. + auto matchOne = [&]() { + if (pattern.front() == '*') { + // Move the pattern iterator past the '*'s. + pattern.remove_prefix(1); + isDoubleStar = false; + while (!pattern.empty() && pattern.front() == '*') + { + pattern.remove_prefix(1); + isDoubleStar = true; + } + // Save the positions where we started the "*" segment + // to be used by backtracking if we see a mismatch later. + patternStarSuffix = pattern; + starStr = str; + bracketsStarMatchIdx = bracketsIdx; return true; } - for (std::size_t i = 0; i <= str.size(); ++i) + + if (pattern.front() == '[') { - if (globMatch(pattern.substr(1), str.substr(i))) + // Match a bracket expression. The parsed bracket expression is + // stored in brackets_ at the current bracketsIdx. + if (CharBracket const& bracketExp = brackets_[bracketsIdx]; + bracketExp.match(str.front())) { + pattern = std::string_view(pattern_.data()).substr(bracketExp.next_offset_); + ++bracketsIdx; + str.remove_prefix(1); return true; } + return false; + } + + if (pattern.front() == '\\') + { + // Match an escaped character. + pattern.remove_prefix(1); + if (pattern.front() == str.front()) + { + pattern.remove_prefix(1); + str.remove_prefix(1); + return true; + } + return false; + } + + if (pattern.front() == str.front() || pattern.front() == '?') + { + // Match a literal character or a '?'. + pattern.remove_prefix(1); + str.remove_prefix(1); + return true; + } + + return false; + }; + + while (!str.empty()) + { + // Keep matching characters until we hit a "*" in the pattern. + if (!pattern.empty() && + matchOne()) + { + continue; + } + + // There is no previous "*" to account for this mismatch. + if (!patternStarSuffix) + { + return MatchType::MISMATCH; + } + + // The new suffix would assume a delimiter matched by the "*" + if (!isDoubleStar && starStr.front() == delimiter) + { + return MatchType::MISMATCH; } + + // Backtrack + pattern = *patternStarSuffix; + starStr.remove_prefix(1); + str = starStr; + bracketsIdx = bracketsStarMatchIdx; + } + + // All bytes in str have been matched. + // Return true if the rest of pattern is empty or contains only '*'s. + if (pattern.find_first_not_of('*') == std::string::npos) + { + return MatchType::FULL; + } + return MatchType::PARTIAL; +} +} // (anon) + +struct GlobPattern::Impl { + std::string pattern; + std::string prefix; + llvm::SmallVector subGlobs; +}; + +Expected +GlobPattern:: +create( + std::string_view const pattern, + std::optional maxSubGlobs) +{ + if (pattern.empty()) + { + return {}; + } + + GlobPattern res; + res.impl_ = std::make_unique(); + + // Store the original pattern. + res.impl_->pattern = std::string(pattern); + + // Store the prefix that does not contain any metacharacter. + std::size_t const prefixSize = pattern.find_first_of("?*[{\\"); + res.impl_->prefix = pattern.substr(0, prefixSize); + if (prefixSize == std::string::npos) + { + // The pattern does not contain any metacharacter. + return res; + } + + // Parse the glob subpatterns. + std::string_view const suffix = pattern.substr(prefixSize); + MRDOCS_TRY( + auto const subPatterns, + parseBraceExpansions(suffix, maxSubGlobs)); + for (std::string_view subPattern: subPatterns) + { + MRDOCS_TRY(auto subGlob, SubGlobPattern::create(subPattern)); + res.impl_->subGlobs.push_back(std::move(subGlob)); + } + return res; +} + +GlobPattern:: +~GlobPattern() = default; + +GlobPattern:: +GlobPattern() = default; + +GlobPattern:: +GlobPattern(GlobPattern const& other) + : impl_(other.impl_ ? std::make_unique(*other.impl_) : nullptr) +{} + +GlobPattern:: +GlobPattern(GlobPattern&& other) noexcept + : impl_(std::move(other.impl_)) +{} + +GlobPattern& +GlobPattern:: +operator=(GlobPattern const& other) +{ + if (this != &other) + { + impl_ = other.impl_ ? std::make_unique(*other.impl_) : nullptr; + } + return *this; +} + +GlobPattern& +GlobPattern:: +operator=(GlobPattern&& other) noexcept +{ + if (this != &other) + { + impl_ = std::move(other.impl_); + } + return *this; +} + +bool +GlobPattern:: +match(std::string_view const str, char const delimiter) const +{ + if (!impl_) + { + return str.empty(); + } + if (!str.starts_with(impl_->prefix)) + { return false; } - if (str.empty()) + std::string_view const suffix = str.substr(impl_->prefix.size()); + if (impl_->subGlobs.empty() && suffix.empty()) + { + return true; + } + for (auto& subGlob: impl_->subGlobs) + { + if (auto m = subGlob.match(suffix, delimiter); + m == SubGlobPattern::MatchType::FULL) + { + return true; + } + } + return false; +} + +bool +GlobPattern:: +matchPatternPrefix(std::string_view const str, char const delimiter) const +{ + if (!impl_) + { + return str.empty(); + } + if (str.size() < impl_->prefix.size()) + { + return impl_->prefix.starts_with(str); + } + if (!str.starts_with(impl_->prefix)) { return false; } - if (pattern[0] == str[0] || pattern[0] == '?') + std::string_view const suffix = str.substr(impl_->prefix.size()); + if (impl_->subGlobs.empty() && suffix.empty()) + { + return true; + } + for (auto& subGlob: impl_->subGlobs) { - return globMatch(pattern.substr(1), str.substr(1)); + if (auto m = subGlob.match(suffix, delimiter); + m == SubGlobPattern::MatchType::FULL || m == SubGlobPattern::MatchType::PARTIAL) + { + return true; + } } return false; } -} // mrdocs -} // clang +std::string_view +GlobPattern:: +pattern() const +{ + if (!impl_) + { + return {}; + } + return impl_->pattern; +} + +} // clang::mrdocs + diff --git a/src/lib/Support/Handlebars.cpp b/src/lib/Support/Handlebars.cpp index 57df69f61d..660abdb5db 100644 --- a/src/lib/Support/Handlebars.cpp +++ b/src/lib/Support/Handlebars.cpp @@ -6342,7 +6342,7 @@ registerContainerHelpers(Handlebars& hbs) for (std::size_t i = 0; i < n; ++i) { res.emplace_back(arr.at(i)); } - std::ranges::sort(res, [&key](dom::Value const& a, dom::Value const& b) + std::ranges::stable_sort(res, [&key](dom::Value const& a, dom::Value const& b) { // If the value is not an object, then we can't sort it // by a key, so we just sort it by the value itself @@ -6386,6 +6386,118 @@ registerContainerHelpers(Handlebars& hbs) hbs.registerHelper("sort_by", sort_by_fn); + static auto filter_by_fn = dom::makeVariadicInvocable([]( + dom::Array const& arguments) -> dom::Value + { + dom::Value container = arguments.at(0); + std::vector keys; + for (std::size_t i = 1; i < arguments.size() - 1; ++i) + { + dom::Value key = arguments.at(i); + keys.push_back(key); + } + + // Given an array of objects, filter these objects by values at + // a given key. If the value at that key returns true, then the + // object is included in the result. + if (container.isArray()) + { + auto const& arr = container.getArray(); + + std::vector res; + std::size_t const n = arr.size(); + for (std::size_t i = 0; i < n; ++i) { + dom::Value el = arr.at(i); + + // If the value is not an object, then we can't filter it + // by a key + if (!el.isObject()) + { + continue; + } + + // If the value is an object but the key doesn't exist, + // then we can't filter it by a key, so we just filter it by + // whichever has the key + auto matchIt = std::ranges::find_if(keys, [&](dom::Value const& key) + { + return + el.getObject().exists(key.getString()) && + el.getObject().get(key.getString()).isTruthy(); + }); + if (bool const matchAny = matchIt != keys.end(); + !matchAny) + { + continue; + } + + res.emplace_back(el); + } + return dom::Array(res); + } + + // If the value is not an array, then we can't filter it + return container; + }); + + hbs.registerHelper("filter_by", filter_by_fn); + + static auto any_of_by_fn = dom::makeVariadicInvocable([]( + dom::Array const& arguments) -> dom::Value + { + dom::Value container = arguments.at(0); + std::vector keys; + for (std::size_t i = 1; i < arguments.size() - 1; ++i) + { + dom::Value key = arguments.at(i); + keys.push_back(key); + } + + // Given an array of objects, any_of these objects by values at + // a given key. If the value at that key returns true, then the + // object is included in the result. + if (container.isArray()) + { + auto const& arr = container.getArray(); + + std::vector res; + std::size_t const n = arr.size(); + for (std::size_t i = 0; i < n; ++i) { + dom::Value el = arr.at(i); + + // If the value is not an object, then we can't any_of it + // by a key + if (!el.isObject()) + { + continue; + } + + // If the value is an object but the key doesn't exist, + // then we can't any_of it by a key, so we just any_of it by + // whichever has the key + auto matchIt = std::ranges::find_if(keys, [&](dom::Value const& key) + { + return + el.getObject().exists(key.getString()) && + el.getObject().get(key.getString()).isTruthy(); + }); + if (bool const matchAny = matchIt != keys.end(); + !matchAny) + { + continue; + } + + return true; + } + return false; + } + + // If the value is not an array, then we can't any_of it + return container; + }); + + hbs.registerHelper("any_of_by", any_of_by_fn); + hbs.registerHelper("at", dom::makeInvocable(at_fn)); static auto fill_fn = dom::makeInvocable([]( @@ -6791,9 +6903,10 @@ registerMathHelpers(Handlebars& hbs) void Handlebars:: unregisterHelper(std::string_view name) { - auto it = helpers_.find(name); - if (it != helpers_.end()) + if (auto it = helpers_.find(name); it != helpers_.end()) + { helpers_.erase(it); + } // Re-register mandatory helpers if (name == "helperMissing") diff --git a/src/lib/Support/Path.cpp b/src/lib/Support/Path.cpp index 6edd6ad477..0dd40b2c96 100644 --- a/src/lib/Support/Path.cpp +++ b/src/lib/Support/Path.cpp @@ -173,8 +173,8 @@ getParentDir( std::string getParentDir( - std::string_view pathName, - unsigned levels) + std::string_view const pathName, + unsigned const levels) { std::string res(pathName); for (unsigned i = 0; i < levels; ++i) diff --git a/src/lib/Support/String.cpp b/src/lib/Support/String.cpp new file mode 100644 index 0000000000..8959faced3 --- /dev/null +++ b/src/lib/Support/String.cpp @@ -0,0 +1,28 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include + +namespace clang::mrdocs { + +void +replace(std::string& s, std::string_view from, std::string_view to) +{ + if (from.empty()) + return; + + size_t start_pos = 0; + while ((start_pos = s.find(from, start_pos)) != std::string::npos) { + s.replace(start_pos, from.length(), to); + start_pos += to.length(); + } +} + +} // clang::mrdocs diff --git a/src/test/Support/Glob.cpp b/src/test/Support/Glob.cpp new file mode 100644 index 0000000000..701fd9a875 --- /dev/null +++ b/src/test/Support/Glob.cpp @@ -0,0 +1,562 @@ +// +// 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) 2024 Alan de Freitas (alandefreitas@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include + +namespace clang::mrdocs { + +struct Glob_test +{ + void + run() + { + // empty + { + // default constructed + { + PathGlobPattern glob; + BOOST_TEST(glob.pattern().empty()); + BOOST_TEST(glob.match("")); + BOOST_TEST_NOT(glob.match("a")); + } + + // empty string + { + auto globExp = PathGlobPattern::create(""); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.pattern().empty()); + } + } + + // literal + { + auto globExp = PathGlobPattern::create("abc"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST_NOT(glob.pattern().empty()); + BOOST_TEST(glob.match("abc")); + BOOST_TEST_NOT(glob.match("abcd")); + BOOST_TEST_NOT(glob.match("a/b/c")); + } + + // "*" + { + // surrounded "*" + { + // Path + { + auto globExp = PathGlobPattern::create("abc*ghi"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abcdefghi")); + BOOST_TEST(glob.match("abcdefghghi")); + BOOST_TEST_NOT(glob.match("abcdefg/ghi")); + BOOST_TEST(glob.match("abcdefg::ghi")); + } + + // Symbol + { + auto globExp = SymbolGlobPattern::create("abc*ghi"); + BOOST_TEST(globExp); + SymbolGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abcdefghi")); + BOOST_TEST(glob.match("abcdefghghi")); + BOOST_TEST(glob.match("abcdefg/ghi")); + BOOST_TEST_NOT(glob.match("abcdefg::ghi")); + } + } + + // single "*" + { + // Path + { + auto globExp = PathGlobPattern::create("*"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("")); + BOOST_TEST(glob.match("abc")); + BOOST_TEST_NOT(glob.match("a/b/c")); + BOOST_TEST(glob.match("a::b::c")); + } + + // Symbol + { + auto globExp = SymbolGlobPattern::create("*"); + BOOST_TEST(globExp); + SymbolGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("")); + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a/b/c")); + BOOST_TEST_NOT(glob.match("a::b::c")); + } + } + + // multiple "*" + { + // Path + { + auto globExp = PathGlobPattern::create("a*b*c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST_NOT(glob.match("a/b/c")); + BOOST_TEST(glob.match("a::b::c")); + BOOST_TEST(glob.match("aabbc")); + } + + // Symbol + { + auto globExp = SymbolGlobPattern::create("a*b*c"); + BOOST_TEST(globExp); + SymbolGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a/b/c")); + BOOST_TEST_NOT(glob.match("a::b::c")); + BOOST_TEST(glob.match("aabbc")); + } + } + + // escaped "*" + { + auto globExp = PathGlobPattern::create("a\\*b"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a*b")); + BOOST_TEST_NOT(glob.match("aab")); + } + } + + + // "**" + { + // surrounded "**" + { + auto globExp = PathGlobPattern::create("abc**ghi"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abcdefghi")); + BOOST_TEST(glob.match("abcdefghghi")); + BOOST_TEST(glob.match("abcdefg/ghi")); + } + + // single "**" + { + auto globExp = PathGlobPattern::create("**"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("")); + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a/b/c")); + } + } + + // single "?" + { + auto globExp = PathGlobPattern::create("a?c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a/c")); + } + + // character sets "[...]" + { + // charset single char [] + { + auto globExp = PathGlobPattern::create("a[b]c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST_NOT(glob.match("acc")); + } + + // charset two chars [] + { + auto globExp = PathGlobPattern::create("a[bc]d"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abd")); + BOOST_TEST(glob.match("acd")); + BOOST_TEST_NOT(glob.match("aad")); + } + + // charset multiple chars [] + { + auto globExp = PathGlobPattern::create("a[bcdef]g"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abg")); + BOOST_TEST(glob.match("acg")); + BOOST_TEST(glob.match("adg")); + BOOST_TEST(glob.match("aeg")); + BOOST_TEST(glob.match("afg")); + BOOST_TEST_NOT(glob.match("agg")); + } + + // single char range - + { + auto globExp = PathGlobPattern::create("a[b-d]e"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abe")); + BOOST_TEST(glob.match("ace")); + BOOST_TEST(glob.match("ade")); + BOOST_TEST_NOT(glob.match("aae")); + } + + // double char range [--] + { + auto globExp = PathGlobPattern::create("a[b-df-h]g"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abg")); + BOOST_TEST(glob.match("acg")); + BOOST_TEST(glob.match("adg")); + BOOST_TEST_NOT(glob.match("aeg")); + BOOST_TEST(glob.match("afg")); + BOOST_TEST(glob.match("agg")); + BOOST_TEST(glob.match("ahg")); + BOOST_TEST_NOT(glob.match("aig")); + } + + // escaped range + { + // escaping with backslash + { + auto globExp = PathGlobPattern::create("a\\[b]c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a[b]c")); + } + + // escaping with set containing only "[" + { + auto globExp = PathGlobPattern::create("a[[]b]c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a[b]c")); + } + } + + // escaped empty range + { + // escaping with backslash + { + auto globExp = PathGlobPattern::create("a\\[]b"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a[]b")); + } + + // escaping with set containing only "[" + { + auto globExp = PathGlobPattern::create("a[[]]b"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a[]b")); + } + } + + // - at the end as part of the set + { + auto globExp = PathGlobPattern::create("a[b-]c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a-c")); + BOOST_TEST_NOT(glob.match("a]c")); + } + + // - at the beginning as part of the set + { + auto globExp = PathGlobPattern::create("a[-b]c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST(glob.match("a-c")); + BOOST_TEST_NOT(glob.match("a]c")); + } + + // range with surrounding set + { + auto globExp = PathGlobPattern::create("a[bc-de]f"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abf")); + BOOST_TEST(glob.match("acf")); + BOOST_TEST(glob.match("adf")); + BOOST_TEST(glob.match("aef")); + BOOST_TEST_NOT(glob.match("aff")); + } + + // invalid cases + { + // empty range + BOOST_TEST_NOT(PathGlobPattern::create("a[]b")); + // unmatched '[' + BOOST_TEST_NOT(PathGlobPattern::create("a[b")); + // range with end lower than start + BOOST_TEST_NOT(PathGlobPattern::create("a[b-a]c")); + } + } + + // negated charset + { + // negated with ^ + { + auto globExp = PathGlobPattern::create("a[^bc]d"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("aad")); + BOOST_TEST(glob.match("aed")); + BOOST_TEST_NOT(glob.match("abd")); + BOOST_TEST_NOT(glob.match("acd")); + } + + // negated with ! + { + auto globExp = PathGlobPattern::create("a[!bc]d"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("aad")); + BOOST_TEST(glob.match("aed")); + BOOST_TEST_NOT(glob.match("abd")); + BOOST_TEST_NOT(glob.match("acd")); + } + + // negated char range + { + auto globExp = PathGlobPattern::create("a[^b-d]e"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("aae")); + BOOST_TEST_NOT(glob.match("abe")); + BOOST_TEST_NOT(glob.match("ace")); + BOOST_TEST_NOT(glob.match("ade")); + BOOST_TEST(glob.match("aee")); + } + } + + // escaping + { + // escaping * + { + auto globExp = PathGlobPattern::create("a\\*b"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a*b")); + BOOST_TEST_NOT(glob.match("aab")); + } + + // escaping literal + { + auto globExp = PathGlobPattern::create("a\\bc"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abc")); + BOOST_TEST_NOT(glob.match("a\\bc")); + } + + // escaping ? + { + auto globExp = PathGlobPattern::create("a\\?c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a?c")); + BOOST_TEST_NOT(glob.match("aac")); + } + + // unescaping + { + auto globExp = PathGlobPattern::create("a\\\\b"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a\\b")); + BOOST_TEST_NOT(glob.match("aab")); + } + + // invalid + { + // stray \ at the end + BOOST_TEST_NOT(PathGlobPattern::create("a\\")); + } + } + + // brace expansion "{,...}" + { + // simple expansion + { + auto globExp = PathGlobPattern::create("a{b,c}d"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abd")); + BOOST_TEST(glob.match("acd")); + BOOST_TEST_NOT(glob.match("aad")); + } + + // escaped { + { + auto globExp = PathGlobPattern::create("a\\{b,c}d"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("a{b,c}d")); + BOOST_TEST_NOT(glob.match("abd")); + } + + // expansion with charsets + { + auto globExp = PathGlobPattern::create("a{b[cd],e}f"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abcf")); + BOOST_TEST(glob.match("abdf")); + BOOST_TEST(glob.match("aef")); + BOOST_TEST_NOT(glob.match("aacf")); + } + + // "," after literal prefix but outside brace expression + { + auto globExp = PathGlobPattern::create("ab{c,d}e,f"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abce,f")); + BOOST_TEST(glob.match("abde,f")); + } + + // "}" after literal prefix but outside brace expression + { + auto globExp = PathGlobPattern::create("ab{c,d}e}f"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abce}f")); + BOOST_TEST(glob.match("abde}f")); + } + + // invalid + { + // unmatched '[' in expansion + BOOST_TEST_NOT(PathGlobPattern::create("a{b[cd,e}f")); + // nested brace expansions + BOOST_TEST_NOT(PathGlobPattern::create("a{b{c,d}}e")); + // brace expansion with no terms + BOOST_TEST_NOT(PathGlobPattern::create("a{}b")); + // brace expansion with single term + BOOST_TEST_NOT(PathGlobPattern::create("a{b}c")); + // stray \\ in glob pattern + BOOST_TEST_NOT(PathGlobPattern::create("ab{c,d}\\")); + // incomplete brace expansion + BOOST_TEST_NOT(PathGlobPattern::create("a{b,c")); + } + } + + // max sub patterns + { + // max sub patterns exceeded with single brace expansion + { + BOOST_TEST_NOT(PathGlobPattern::create("a{b,c,d}z", 2)); + } + + // max sub patterns exceeded with multiple brace expansions + { + BOOST_TEST_NOT(PathGlobPattern::create("a{b,c,d}{e,f,g}z", 5)); + } + + // max sub patterns not exceeded with single brance expansion + { + auto globExp = PathGlobPattern::create("a{b,c,d}z", 4); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abz")); + BOOST_TEST(glob.match("acz")); + BOOST_TEST(glob.match("adz")); + BOOST_TEST_NOT(glob.match("aez")); + } + + // max sub patterns not exceeded with multiple brace expansions + { + auto globExp = PathGlobPattern::create("a{b,c,d}{e,f,g}z", 9); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.match("abez")); + BOOST_TEST(glob.match("abfz")); + BOOST_TEST(glob.match("abgz")); + BOOST_TEST(glob.match("acez")); + BOOST_TEST(glob.match("acfz")); + BOOST_TEST(glob.match("acgz")); + BOOST_TEST(glob.match("adez")); + BOOST_TEST(glob.match("adfz")); + BOOST_TEST(glob.match("adgz")); + BOOST_TEST_NOT(glob.match("aehz")); + } + } + + // matchPrefix + { + // empty + { + auto globExp = PathGlobPattern::create(""); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.matchPatternPrefix("")); + } + + // literal + { + auto globExp = PathGlobPattern::create("abc"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.matchPatternPrefix("")); + BOOST_TEST(glob.matchPatternPrefix("a")); + BOOST_TEST(glob.matchPatternPrefix("ab")); + BOOST_TEST(glob.matchPatternPrefix("abc")); + BOOST_TEST_NOT(glob.matchPatternPrefix("c")); + BOOST_TEST_NOT(glob.matchPatternPrefix("abcd")); + } + + // star + { + auto globExp = PathGlobPattern::create("a*c"); + BOOST_TEST(globExp); + PathGlobPattern const& glob = *globExp; + BOOST_TEST(glob.matchPatternPrefix("")); + BOOST_TEST(glob.matchPatternPrefix("a")); + BOOST_TEST(glob.matchPatternPrefix("ab")); + BOOST_TEST(glob.matchPatternPrefix("abc")); + BOOST_TEST(glob.matchPatternPrefix("ac")); + BOOST_TEST(glob.matchPatternPrefix("ad")); + BOOST_TEST(glob.matchPatternPrefix("adc")); + BOOST_TEST_NOT(glob.matchPatternPrefix("b")); + } + + // star with delimiters + { + auto globExp = SymbolGlobPattern::create("ns::c::*"); + BOOST_TEST(globExp); + SymbolGlobPattern const& glob = *globExp; + BOOST_TEST(glob.matchPatternPrefix("")); + BOOST_TEST(glob.matchPatternPrefix("ns")); + BOOST_TEST(glob.matchPatternPrefix("ns::")); + BOOST_TEST(glob.matchPatternPrefix("ns::c")); + BOOST_TEST(glob.matchPatternPrefix("ns::c::")); + BOOST_TEST(glob.matchPatternPrefix("ns::c::d")); + BOOST_TEST_NOT(glob.matchPatternPrefix("std")); + } + } + } +}; + +TEST_SUITE( + Glob_test, + "clang.mrdocs.Glob"); + +} // clang::mrdocs + diff --git a/src/test/TestMain.cpp b/src/test/TestMain.cpp index 7983f383a1..20342cfc12 100644 --- a/src/test/TestMain.cpp +++ b/src/test/TestMain.cpp @@ -32,9 +32,11 @@ void DoTestAction(char const** argv) { using namespace clang::mrdocs; - TestRunner runner(testArgs.generate); - for(auto const& inputPath : testArgs.inputs) + TestRunner runner(testArgs.generator); + for (auto const& inputPath: testArgs.cmdLineInputs) + { runner.checkPath(inputPath, argv); + } auto const& results = runner.results; std::stringstream os; @@ -50,6 +52,8 @@ void DoTestAction(char const** argv) case Action::update: os << "Update action: "; break; + default: + MRDOCS_UNREACHABLE(); } os << @@ -74,9 +78,10 @@ int test_main(int argc, char const** argv) llvm::setBugReportMsg("PLEASE submit a bug report to https://github.com/cppalliance/mrdocs/issues/ and include the crash backtrace.\n"); testArgs.hideForeignOptions(); - if(! llvm::cl::ParseCommandLineOptions( - argc, argv, testArgs.usageText)) + if (!llvm::cl::ParseCommandLineOptions(argc, argv, testArgs.usageText)) + { return EXIT_FAILURE; + } // Apply reportLevel report::setMinimumLevel(report::getLevel( @@ -84,15 +89,21 @@ int test_main(int argc, char const** argv) report::setSourceLocationWarnings(false); - if(!testArgs.inputs.empty()) + if (!testArgs.cmdLineInputs.empty()) + { DoTestAction(argv); + } - if(testArgs.unitOption.getValue()) + if (testArgs.unitOption.getValue()) + { test_suite::unit_test_main(argc, argv); + } - if( report::results.errorCount > 0 || + if (report::results.errorCount > 0 || report::results.fatalCount > 0) + { return EXIT_FAILURE; + } return EXIT_SUCCESS; } diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index bc9ab3edd2..2e88acc5a6 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -25,12 +25,10 @@ #include #include #include -#include #include #include -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { TestRunner:: TestRunner(std::string_view generator) @@ -91,11 +89,16 @@ handleFile( Config::Settings fileSettings = dirSettings; auto configPath = files::withExtension(filePath, "yml"); if (files::exists(configPath)) { - Config::Settings::load_file(fileSettings, configPath, dirs_).value(); - fileSettings.normalize(dirs_); + if (auto exp = Config::Settings::load_file(fileSettings, configPath, dirs_); !exp) + { + return report::error("Failed to load config file: {}: \"{}\"", exp.error(), configPath); + } + if (auto exp = fileSettings.normalize(dirs_); !exp) + { + return report::error("Failed to normalize config file: {}: \"{}\"", exp.error(), configPath); + } } - // Config Implementation std::shared_ptr config = ConfigImpl::load(fileSettings, dirs_, threadPool_).value(); @@ -111,10 +114,13 @@ handleFile( llvm::StringRef(parentDir), SingleFileDB(filePath), config, defaultIncludePaths); report::setMinimumLevel(report::Level::error); + // Build Corpus auto corpus = CorpusImpl::build(config, compilations); - if(! corpus) + if (!corpus) + { return report::error("{}: \"{}\"", corpus.error(), filePath); + } // Generate std::string generatedDocs; @@ -128,10 +134,14 @@ handleFile( std::unique_ptr expectedDocsBuf; { auto fileResult = llvm::MemoryBuffer::getFile(expectedPath, false, true, true); - if(fileResult) + if (fileResult) + { expectedDocsBuf = std::move(fileResult.get()); - else if(fileResult.getError() != std::errc::no_such_file_or_directory) - return report::error("{}: \"{}\"", fileResult.getError(), expectedPath); + } else if (fileResult.getError() != std::errc::no_such_file_or_directory) + { + return report:: + error("{}: \"{}\"", fileResult.getError(), expectedPath); + } } // If no expected documentation file @@ -257,11 +267,14 @@ handleDir( std::string const& configPath = files::appendPath(entry.path(), "mrdocs.yml"); if (files::exists(configPath)) { - Config::Settings::load_file(subdirSettings, configPath, dirs_).value(); - auto prev = dirs_.configDir; - dirs_.configDir = entry.path(); - subdirSettings.normalize(dirs_); - dirs_.configDir = std::move(prev); + if (auto exp = Config::Settings::load_file(subdirSettings, configPath, dirs_); !exp) + { + return report::error("Failed to load config file: {}: \"{}\"", exp.error(), configPath); + } + if (auto exp = subdirSettings.normalize(dirs_); !exp) + { + return report::error("Failed to normalize config file: {}: \"{}\"", exp.error(), configPath); + } } handleDir(entry.path(), subdirSettings); } @@ -304,8 +317,7 @@ checkPath( std::string const inputDir = fileType == files::FileType::directory ? inputPath : files::getParentDir(inputPath); - dirs_.configDir = inputDir; - dirs_.cwd = dirs_.configDir; + dirs_.cwd = inputDir; // Check for a directory-wide config Config::Settings dirSettings; @@ -314,10 +326,15 @@ checkPath( dirSettings.sourceRoot = files::appendPath(inputPath, "."); std::string const& configPath = files::appendPath(inputDir, "mrdocs.yml"); - if (files::exists(configPath)) { - Config::Settings::load_file(dirSettings, configPath, dirs_).value(); - if (auto exp = dirSettings.normalize(dirs_); !exp) { - return report::error("{}: \"{}\"", exp.error(), configPath); + if (files::exists(configPath)) + { + if (auto exp = Config::Settings::load_file(dirSettings, configPath, dirs_); !exp) + { + return report::error("Failed to load config file: {}: \"{}\"", exp.error(), configPath); + } + if (auto exp = dirSettings.normalize(dirs_); !exp) + { + return report::error("Failed to normalize config file: {}: \"{}\"", exp.error(), configPath); } } @@ -326,7 +343,7 @@ checkPath( case files::FileType::regular: { // Require a .cpp file - if(! path::extension(inputPath).equals_insensitive(".cpp")) + if (!path::extension(inputPath).equals_insensitive(".cpp")) { Error err("not a .cpp file"); return report::error("{}: \"{}\"", @@ -358,5 +375,4 @@ checkPath( } } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/test/lib/Dom/LazyObject.cpp b/src/test/lib/Dom/LazyObject.cpp index bbc4960c08..5b504ce93f 100644 --- a/src/test/lib/Dom/LazyObject.cpp +++ b/src/test/lib/Dom/LazyObject.cpp @@ -207,7 +207,8 @@ struct LazyObject_test // stop visiting { std::size_t count = 0; - obj.visit([&count](String key, Value value) { + obj.visit([&count](String, Value) + { ++count; return false; }); diff --git a/src/tool/GenerateAction.cpp b/src/tool/GenerateAction.cpp index 8bbab509a8..0724fdd1e7 100644 --- a/src/tool/GenerateAction.cpp +++ b/src/tool/GenerateAction.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -113,10 +114,10 @@ DoGenerateAction( auto& settings = config->settings(); MRDOCS_TRY( Generator const& generator, - getGenerators().find(to_string(settings.generate)), + getGenerators().find(to_string(settings.generator)), formatError( "the Generator \"{}\" was not found", - to_string(config->settings().generate))); + to_string(config->settings().generator))); // -------------------------------------------------------------- // diff --git a/src/tool/ToolMain.cpp b/src/tool/ToolMain.cpp index 2b6728ea60..004691b6eb 100644 --- a/src/tool/ToolMain.cpp +++ b/src/tool/ToolMain.cpp @@ -65,7 +65,7 @@ getReferenceDirectories(std::string const& execPath) } else { - llvm::cl::list& inputs = toolArgs.inputs; + llvm::cl::list& inputs = toolArgs.cmdLineInputs; for (auto& input: inputs) { if (files::getFileName(input) == "mrdocs.yml") @@ -87,7 +87,6 @@ getReferenceDirectories(std::string const& execPath) return Unexpected(formatError("The config path is missing")); } configPath = files::makeAbsolute(configPath, dirs.cwd); - dirs.configDir = files::getParentDir(configPath); return std::make_pair(configPath, dirs); } diff --git a/test-files/golden-tests/class-template-specializations-1.adoc b/test-files/golden-tests/class-template-specializations-1.adoc deleted file mode 100644 index 502e3afa86..0000000000 --- a/test-files/golden-tests/class-template-specializations-1.adoc +++ /dev/null @@ -1,1824 +0,0 @@ -= Reference -:mrdocs: - -[#index] -== Global namespace - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#R0,`R0`>> -| - -| <<#R1,`R1`>> -| - -| <<#R10,`R10`>> -| - -| <<#R11,`R11`>> -| - -| <<#R12,`R12`>> -| - -| <<#R13,`R13`>> -| - -| <<#R14,`R14`>> -| - -| <<#R15,`R15`>> -| - -| <<#R16,`R16`>> -| - -| <<#R17,`R17`>> -| - -| <<#R18,`R18`>> -| - -| <<#R19,`R19`>> -| - -| <<#R2,`R2`>> -| - -| <<#R20,`R20`>> -| - -| <<#R21,`R21`>> -| - -| <<#R22,`R22`>> -| - -| <<#R23,`R23`>> -| - -| <<#R24,`R24`>> -| - -| <<#R25,`R25`>> -| - -| <<#R26,`R26`>> -| - -| <<#R27,`R27`>> -| - -| <<#R28,`R28`>> -| - -| <<#R29,`R29`>> -| - -| <<#R3,`R3`>> -| - -| <<#R30,`R30`>> -| - -| <<#R31,`R31`>> -| - -| <<#R32,`R32`>> -| - -| <<#R33,`R33`>> -| - -| <<#R34,`R34`>> -| - -| <<#R35,`R35`>> -| - -| <<#R36,`R36`>> -| - -| <<#R37,`R37`>> -| - -| <<#R38,`R38`>> -| - -| <<#R39,`R39`>> -| - -| <<#R4,`R4`>> -| - -| <<#R40,`R40`>> -| - -| <<#R41,`R41`>> -| - -| <<#R42,`R42`>> -| - -| <<#R43,`R43`>> -| - -| <<#R44,`R44`>> -| - -| <<#R45,`R45`>> -| - -| <<#R46,`R46`>> -| - -| <<#R47,`R47`>> -| - -| <<#R48,`R48`>> -| - -| <<#R5,`R5`>> -| - -| <<#R6,`R6`>> -| - -| <<#R7,`R7`>> -| - -| <<#R8,`R8`>> -| - -| <<#R9,`R9`>> -| - -| <<#S0-02,`S0<1, int*>`>> -| - -| <<#S0-0c4,`S0<1, T*>`>> -| - -| <<#S0-0be,`S0<0>`>> -| - -| <<#S0-0cf,`S0`>> -| - -|=== - -[#S0-0cf] -== S0 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T = void> -struct S0; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1,`S1`>> -| - -| <<#S0-0cf-S5,`S5`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-f0,`f0`>> -| - -|=== - - - -[#S0-0cf-f0] -== <<#S0-0cf,S0>>::f0 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f0(); ----- - -[#S0-0cf-S1] -== <<#S0-0cf,S0>>::S1 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct S1; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2,`S2`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-f1,`f1`>> -| - -|=== - - - -[#S0-0cf-S1-f1] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::f1 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f1(); ----- - -[#S0-0cf-S1-S2] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::S2 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int J, - typename U = void> -struct S2; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3,`S3`>> -| - -| <<#S0-0cf-S1-S2-S4,`S4`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-f2,`f2`>> -| - -|=== - - - -[#S0-0cf-S1-S2-f2] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>>::f2 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f2(); ----- - -[#S0-0cf-S1-S2-S3] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>>::S3 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct S3; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3-f3,`f3`>> -| - -|=== - - - -[#S0-0cf-S1-S2-S3-f3] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>>::<<#S0-0cf-S1-S2-S3,S3>>::f3 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f3(); ----- - -[#S0-0cf-S1-S2-S4] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>>::S4 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int K, - typename V = void> -struct S4; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S4-f4,`f4`>> -| - -|=== - - - -[#S0-0cf-S1-S2-S4-f4] -== <<#S0-0cf,S0>>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>>::<<#S0-0cf-S1-S2-S4,S4>>::f4 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f4(); ----- - -[#S0-0cf-S5] -== <<#S0-0cf,S0>>::S5 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int J, - typename U = void> -struct S5; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6,`S6`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-f5,`f5`>> -| - -|=== - - - -[#S0-0cf-S5-f5] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::f5 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f5(); ----- - -[#S0-0cf-S5-S6] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::S6 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct S6; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7,`S7`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-f6,`f6`>> -| - -|=== - - - -[#S0-0cf-S5-S6-f6] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::f6 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f6(); ----- - -[#S0-0cf-S5-S6-S7] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::S7 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int K, - typename V = void> -struct S7; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8,`S8`>> -| - -| <<#S0-0cf-S5-S6-S7-S9,`S9`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-f7,`f7`>> -| - -|=== - - - -[#S0-0cf-S5-S6-S7-f7] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>>::f7 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f7(); ----- - -[#S0-0cf-S5-S6-S7-S8] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>>::S8 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct S8; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8-f8,`f8`>> -| - -|=== - - - -[#S0-0cf-S5-S6-S7-S8-f8] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>>::<<#S0-0cf-S5-S6-S7-S8,S8>>::f8 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f8(); ----- - -[#S0-0cf-S5-S6-S7-S9] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>>::S9 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int L, - typename W = void> -struct S9; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S9-f9,`f9`>> -| - -|=== - - - -[#S0-0cf-S5-S6-S7-S9-f9] -== <<#S0-0cf,S0>>::<<#S0-0cf-S5,S5>>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>>::<<#S0-0cf-S5-S6-S7-S9,S9>>::f9 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f9(); ----- - -[#S0-0be] -== S0<0> - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<> -struct <<#S0-0cf,S0>><0>; ----- - - - - -[#S0-0c4] -== S0<1, T*> - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<typename T> -struct <<#S0-0cf,S0>><1, T*>; ----- - - - - -[#S0-02] -== S0<1, int*> - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<> -struct <<#S0-0cf,S0>><1, int*>; ----- - - - - -[#R0] -== R0 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R0 - : <<#S0-0cf,S0>><‐1>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1,`S1`>> -| - -| <<#S0-0cf-S5,`S5`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-f0,`f0`>> -| - -|=== - - - -[#R1] -== R1 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R1 - : <<#S0-0be,S0>><0>; ----- - - - - -[#R2] -== R2 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R2 - : <<#S0-0c4,S0>><1, void*>; ----- - - - - -[#R3] -== R3 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R3 - : <<#S0-02,S0>><1, int*>; ----- - - - - -[#R4] -== R4 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R4 - : <<#S0-0cf,S0>><2>::<<#S0-09c-S1,S1>>; ----- - - - - -[#R5] -== R5 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R5 - : <<#S0-0cf,S0>><3>::<<#S0-0cf-S1,S1>>::<<#S0-073-S1-S2,S2>><‐1>; ----- - - - - -[#R6] -== R6 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R6 - : <<#S0-0cf,S0>><4>::<<#S0-0cf-S1,S1>>::<<#S0-0a1-S1-S2,S2>><5>; ----- - - - - -[#R7] -== R7 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R7 - : <<#S0-0cf,S0>><6>::<<#S0-0cf-S1,S1>>::<<#S0-07e-S1-S2-06,S2>><7, void*>; ----- - - - - -[#R8] -== R8 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R8 - : <<#S0-0cf,S0>><6>::<<#S0-0cf-S1,S1>>::<<#S0-07e-S1-S2-07,S2>><7, int*>; ----- - - - - -[#R9] -== R9 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R9 - : <<#S0-0cf,S0>><8>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><9>::<<#S0-0a3-S1-S2-S3,S3>>; ----- - - - - -[#R10] -== R10 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R10 - : <<#S0-0cf,S0>><10>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><11>::<<#S0-08-S1-S2-S4,S4>><‐1>; ----- - - - - -[#R11] -== R11 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R11 - : <<#S0-0cf,S0>><12>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><13>::<<#S0-0e-S1-S2-S4,S4>><14>; ----- - - - - -[#R12] -== R12 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R12 - : <<#S0-0cf,S0>><15>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><16>::<<#S0-09e4-S1-S2-S4-07,S4>><17, void*>; ----- - - - - -[#R13] -== R13 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R13 - : <<#S0-0cf,S0>><15>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><16>::<<#S0-09e4-S1-S2-S4-02,S4>><17, int*>; ----- - - - - -[#R14] -== R14 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R14 - : <<#S0-0cf,S0>><18>::<<#S0-07a-S5,S5>><‐1>; ----- - - - - -[#R15] -== R15 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R15 - : <<#S0-0cf,S0>><19>::<<#S0-0a7-S5,S5>><20>; ----- - - - - -[#R16] -== R16 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R16 - : <<#S0-0cf,S0>><21>::<<#S0-031-S5-0b,S5>><22, void*>; ----- - - - - -[#R17] -== R17 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R17 - : <<#S0-0cf,S0>><21>::<<#S0-031-S5-03,S5>><22, int*>; ----- - - - - -[#R18] -== R18 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R18 - : <<#S0-0cf,S0>><23>::<<#S0-0cf-S5,S5>><24>::<<#S0-05-S5-S6,S6>>; ----- - - - - -[#R19] -== R19 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R19 - : <<#S0-0cf,S0>><25>::<<#S0-0cf-S5,S5>><26>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0a2-S5-S6-S7,S7>><‐1>; ----- - - - - -[#R20] -== R20 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R20 - : <<#S0-0cf,S0>><27>::<<#S0-0cf-S5,S5>><28>::<<#S0-0cf-S5-S6,S6>>::<<#S0-09e2-S5-S6-S7-0a,S7>><29, void*>; ----- - - - - -[#R21] -== R21 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R21 - : <<#S0-0cf,S0>><27>::<<#S0-0cf-S5,S5>><28>::<<#S0-0cf-S5-S6,S6>>::<<#S0-09e2-S5-S6-S7-0d,S7>><29, int*>; ----- - - - - -[#R22] -== R22 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R22 - : <<#S0-0cf,S0>><30>::<<#S0-0cf-S5,S5>><31>::<<#S0-0cf-S5-S6,S6>>::<<#S0-01-S5-S6-S7,S7>><32>; ----- - - - - -[#R23] -== R23 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R23 - : <<#S0-0cf,S0>><33>::<<#S0-0cf-S5,S5>><34>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><35>::<<#S0-09ee-S5-S6-S7-S8,S8>>; ----- - - - - -[#R24] -== R24 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R24 - : <<#S0-0cf,S0>><36>::<<#S0-0cf-S5,S5>><37>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><38>::<<#S0-033-S5-S6-S7-S9,S9>><‐1>; ----- - - - - -[#R25] -== R25 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R25 - : <<#S0-0cf,S0>><39>::<<#S0-0cf-S5,S5>><40>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><41>::<<#S0-06-S5-S6-S7-S9-00,S9>><42, void*>; ----- - - - - -[#R26] -== R26 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R26 - : <<#S0-0cf,S0>><39>::<<#S0-0cf-S5,S5>><40>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><41>::<<#S0-06-S5-S6-S7-S9-08,S9>><42, int*>; ----- - - - - -[#R27] -== R27 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R27 - : <<#S0-0cf,S0>><43>::<<#S0-0cf-S5,S5>><44>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><45>::<<#S0-0ba-S5-S6-S7-S9,S9>><46>; ----- - - - - -[#R28] -== R28 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R28 - : <<#S0-0cf,S0>><0, bool>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1,`S1`>> -| - -| <<#S0-0cf-S5,`S5`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-f0,`f0`>> -| - -|=== - - - -[#R29] -== R29 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R29 - : <<#S0-0cf,S0>><1, int>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1,`S1`>> -| - -| <<#S0-0cf-S5,`S5`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-f0,`f0`>> -| - -|=== - - - -[#R30] -== R30 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R30 - : <<#S0-0cf,S0>><2, bool>::<<#S0-0cf-S1,S1>>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2,`S2`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-f1,`f1`>> -| - -|=== - - - -[#R31] -== R31 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T> -struct R31 - : <<#S0-0cf,S0>><3, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><I, T>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3,`S3`>> -| - -| <<#S0-0cf-S1-S2-S4,`S4`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-f2,`f2`>> -| - -|=== - - - -[#R32] -== R32 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R32 - : <<#S0-0cf,S0>><4, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><5, bool>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3,`S3`>> -| - -| <<#S0-0cf-S1-S2-S4,`S4`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-f2,`f2`>> -| - -|=== - - - -[#R33] -== R33 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R33 - : <<#S0-0cf,S0>><6, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><7, int>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3,`S3`>> -| - -| <<#S0-0cf-S1-S2-S4,`S4`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-f2,`f2`>> -| - -|=== - - - -[#R34] -== R34 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R34 - : <<#S0-0cf,S0>><8, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><9, bool>::<<#S0-0cf-S1-S2-S3,S3>>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S3-f3,`f3`>> -| - -|=== - - - -[#R35] -== R35 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T> -struct R35 - : <<#S0-0cf,S0>><10, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><11, bool>::<<#S0-0cf-S1-S2-S4,S4>><I, T>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S4-f4,`f4`>> -| - -|=== - - - -[#R36] -== R36 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R36 - : <<#S0-0cf,S0>><12, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><13, bool>::<<#S0-0cf-S1-S2-S4,S4>><14, bool>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S4-f4,`f4`>> -| - -|=== - - - -[#R37] -== R37 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R37 - : <<#S0-0cf,S0>><15, bool>::<<#S0-0cf-S1,S1>>::<<#S0-0cf-S1-S2,S2>><16, bool>::<<#S0-0cf-S1-S2-S4,S4>><17, int>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S1-S2-S4-f4,`f4`>> -| - -|=== - - - -[#R38] -== R38 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T> -struct R38 - : <<#S0-0cf,S0>><18, bool>::<<#S0-0cf-S5,S5>><I, T>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6,`S6`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-f5,`f5`>> -| - -|=== - - - -[#R39] -== R39 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R39 - : <<#S0-0cf,S0>><19, bool>::<<#S0-0cf-S5,S5>><20, bool>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6,`S6`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-f5,`f5`>> -| - -|=== - - - -[#R40] -== R40 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R40 - : <<#S0-0cf,S0>><21, bool>::<<#S0-0cf-S5,S5>><22, int>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6,`S6`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-f5,`f5`>> -| - -|=== - - - -[#R41] -== R41 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R41 - : <<#S0-0cf,S0>><23, bool>::<<#S0-0cf-S5,S5>><24, bool>::<<#S0-0cf-S5-S6,S6>>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7,`S7`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-f6,`f6`>> -| - -|=== - - - -[#R42] -== R42 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T> -struct R42 - : <<#S0-0cf,S0>><25, bool>::<<#S0-0cf-S5,S5>><26, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><I, T>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8,`S8`>> -| - -| <<#S0-0cf-S5-S6-S7-S9,`S9`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-f7,`f7`>> -| - -|=== - - - -[#R43] -== R43 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R43 - : <<#S0-0cf,S0>><27, bool>::<<#S0-0cf-S5,S5>><28, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><29, int>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8,`S8`>> -| - -| <<#S0-0cf-S5-S6-S7-S9,`S9`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-f7,`f7`>> -| - -|=== - - - -[#R44] -== R44 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R44 - : <<#S0-0cf,S0>><30, bool>::<<#S0-0cf-S5,S5>><31, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><32, bool>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8,`S8`>> -| - -| <<#S0-0cf-S5-S6-S7-S9,`S9`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-f7,`f7`>> -| - -|=== - - - -[#R45] -== R45 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R45 - : <<#S0-0cf,S0>><33, bool>::<<#S0-0cf-S5,S5>><34, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><35, bool>::<<#S0-0cf-S5-S6-S7-S8,S8>>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S8-f8,`f8`>> -| - -|=== - - - -[#R46] -== R46 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template< - int I, - typename T> -struct R46 - : <<#S0-0cf,S0>><36, bool>::<<#S0-0cf-S5,S5>><37, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><38, bool>::<<#S0-0cf-S5-S6-S7-S9,S9>><I, T>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S9-f9,`f9`>> -| - -|=== - - - -[#R47] -== R47 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R47 - : <<#S0-0cf,S0>><39, bool>::<<#S0-0cf-S5,S5>><40, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><41, bool>::<<#S0-0cf-S5-S6-S7-S9,S9>><42, int>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S9-f9,`f9`>> -| - -|=== - - - -[#R48] -== R48 - -=== Synopsis - -Declared in `<class‐template‐specializations‐1.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct R48 - : <<#S0-0cf,S0>><43, bool>::<<#S0-0cf-S5,S5>><44, bool>::<<#S0-0cf-S5-S6,S6>>::<<#S0-0cf-S5-S6-S7,S7>><45, bool>::<<#S0-0cf-S5-S6-S7-S9,S9>><46, bool>; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#S0-0cf-S5-S6-S7-S9-f9,`f9`>> -| - -|=== - - - - - -[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/class-template-specializations-1.html b/test-files/golden-tests/class-template-specializations-1.html deleted file mode 100644 index 925c1c7eac..0000000000 --- a/test-files/golden-tests/class-template-specializations-1.html +++ /dev/null @@ -1,2135 +0,0 @@ - - -Reference - - -
-

Reference

-
-
-

Global namespace

-
-

Types

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
R0 -
R1 -
R10 -
R11 -
R12 -
R13 -
R14 -
R15 -
R16 -
R17 -
R18 -
R19 -
R2 -
R20 -
R21 -
R22 -
R23 -
R24 -
R25 -
R26 -
R27 -
R28 -
R29 -
R3 -
R30 -
R31 -
R32 -
R33 -
R34 -
R35 -
R36 -
R37 -
R38 -
R39 -
R4 -
R40 -
R41 -
R42 -
R43 -
R44 -
R45 -
R46 -
R47 -
R48 -
R5 -
R6 -
R7 -
R8 -
R9 -
S0<1, int*> -
S0<1, T*> -
S0<0> -
S0 -
-
-
-
-

S0

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T = void>
-struct S0;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S1 -
S5 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f0 -
- - -
-
-
-

S0::f0

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f0();
-
-
-
-
-
-
-

S0::S1

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct S1;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S2 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f1 -
- - -
-
-
-

S0::S1::f1

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f1();
-
-
-
-
-
-
-

S0::S1::S2

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int J,
-    typename U = void>
-struct S2;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S3 -
S4 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f2 -
- - -
-
-
-

S0::S1::S2::f2

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f2();
-
-
-
-
-
-
-

S0::S1::S2::S3

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct S3;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f3 -
- - -
-
-
-

S0::S1::S2::S3::f3

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f3();
-
-
-
-
-
-
-

S0::S1::S2::S4

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int K,
-    typename V = void>
-struct S4;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f4 -
- - -
-
-
-

S0::S1::S2::S4::f4

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f4();
-
-
-
-
-
-
-

S0::S5

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int J,
-    typename U = void>
-struct S5;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S6 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f5 -
- - -
-
-
-

S0::S5::f5

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f5();
-
-
-
-
-
-
-

S0::S5::S6

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct S6;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S7 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f6 -
- - -
-
-
-

S0::S5::S6::f6

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f6();
-
-
-
-
-
-
-

S0::S5::S6::S7

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int K,
-    typename V = void>
-struct S7;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S8 -
S9 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

S0::S5::S6::S7::f7

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f7();
-
-
-
-
-
-
-

S0::S5::S6::S7::S8

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct S8;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f8 -
- - -
-
-
-

S0::S5::S6::S7::S8::f8

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f8();
-
-
-
-
-
-
-

S0::S5::S6::S7::S9

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int L,
-    typename W = void>
-struct S9;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f9 -
- - -
-
-
-

S0::S5::S6::S7::S9::f9

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-void
-f9();
-
-
-
-
-
-
-

S0<0>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<>
-struct S0<0>;
-
-
-
- - -
-
-
-

S0<1, T*>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<typename T>
-struct S0<1, T*>;
-
-
-
- - -
-
-
-

S0<1, int*>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<>
-struct S0<1, int*>;
-
-
-
- - -
-
-
-

R0

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R0
-    : S0<-1>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S1 -
S5 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f0 -
- - -
-
-
-

R1

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R1
-    : S0<0>;
-
-
-
- - -
-
-
-

R2

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R2
-    : S0<1, void*>;
-
-
-
- - -
-
-
-

R3

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R3
-    : S0<1, int*>;
-
-
-
- - -
-
-
-

R4

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R4
-    : S0<2>::S1;
-
-
-
- - -
-
-
-

R5

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R5
-    : S0<3>::S1::S2<-1>;
-
-
-
- - -
-
-
-

R6

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R6
-    : S0<4>::S1::S2<5>;
-
-
-
- - -
-
-
-

R7

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R7
-    : S0<6>::S1::S2<7, void*>;
-
-
-
- - -
-
-
-

R8

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R8
-    : S0<6>::S1::S2<7, int*>;
-
-
-
- - -
-
-
-

R9

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R9
-    : S0<8>::S1::S2<9>::S3;
-
-
-
- - -
-
-
-

R10

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R10
-    : S0<10>::S1::S2<11>::S4<-1>;
-
-
-
- - -
-
-
-

R11

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R11
-    : S0<12>::S1::S2<13>::S4<14>;
-
-
-
- - -
-
-
-

R12

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R12
-    : S0<15>::S1::S2<16>::S4<17, void*>;
-
-
-
- - -
-
-
-

R13

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R13
-    : S0<15>::S1::S2<16>::S4<17, int*>;
-
-
-
- - -
-
-
-

R14

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R14
-    : S0<18>::S5<-1>;
-
-
-
- - -
-
-
-

R15

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R15
-    : S0<19>::S5<20>;
-
-
-
- - -
-
-
-

R16

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R16
-    : S0<21>::S5<22, void*>;
-
-
-
- - -
-
-
-

R17

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R17
-    : S0<21>::S5<22, int*>;
-
-
-
- - -
-
-
-

R18

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R18
-    : S0<23>::S5<24>::S6;
-
-
-
- - -
-
-
-

R19

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R19
-    : S0<25>::S5<26>::S6::S7<-1>;
-
-
-
- - -
-
-
-

R20

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R20
-    : S0<27>::S5<28>::S6::S7<29, void*>;
-
-
-
- - -
-
-
-

R21

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R21
-    : S0<27>::S5<28>::S6::S7<29, int*>;
-
-
-
- - -
-
-
-

R22

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R22
-    : S0<30>::S5<31>::S6::S7<32>;
-
-
-
- - -
-
-
-

R23

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R23
-    : S0<33>::S5<34>::S6::S7<35>::S8;
-
-
-
- - -
-
-
-

R24

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R24
-    : S0<36>::S5<37>::S6::S7<38>::S9<-1>;
-
-
-
- - -
-
-
-

R25

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R25
-    : S0<39>::S5<40>::S6::S7<41>::S9<42, void*>;
-
-
-
- - -
-
-
-

R26

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R26
-    : S0<39>::S5<40>::S6::S7<41>::S9<42, int*>;
-
-
-
- - -
-
-
-

R27

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R27
-    : S0<43>::S5<44>::S6::S7<45>::S9<46>;
-
-
-
- - -
-
-
-

R28

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R28
-    : S0<0, bool>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S1 -
S5 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f0 -
- - -
-
-
-

R29

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R29
-    : S0<1, int>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S1 -
S5 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f0 -
- - -
-
-
-

R30

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R30
-    : S0<2, bool>::S1;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S2 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f1 -
- - -
-
-
-

R31

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T>
-struct R31
-    : S0<3, bool>::S1::S2<I, T>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S3 -
S4 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f2 -
- - -
-
-
-

R32

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R32
-    : S0<4, bool>::S1::S2<5, bool>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S3 -
S4 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f2 -
- - -
-
-
-

R33

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R33
-    : S0<6, bool>::S1::S2<7, int>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S3 -
S4 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f2 -
- - -
-
-
-

R34

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R34
-    : S0<8, bool>::S1::S2<9, bool>::S3;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f3 -
- - -
-
-
-

R35

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T>
-struct R35
-    : S0<10, bool>::S1::S2<11, bool>::S4<I, T>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f4 -
- - -
-
-
-

R36

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R36
-    : S0<12, bool>::S1::S2<13, bool>::S4<14, bool>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f4 -
- - -
-
-
-

R37

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R37
-    : S0<15, bool>::S1::S2<16, bool>::S4<17, int>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f4 -
- - -
-
-
-

R38

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T>
-struct R38
-    : S0<18, bool>::S5<I, T>;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S6 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f5 -
- - -
-
-
-

R39

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R39
-    : S0<19, bool>::S5<20, bool>;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S6 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f5 -
- - -
-
-
-

R40

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R40
-    : S0<21, bool>::S5<22, int>;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S6 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f5 -
- - -
-
-
-

R41

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R41
-    : S0<23, bool>::S5<24, bool>::S6;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
S7 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f6 -
- - -
-
-
-

R42

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T>
-struct R42
-    : S0<25, bool>::S5<26, bool>::S6::S7<I, T>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S8 -
S9 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

R43

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R43
-    : S0<27, bool>::S5<28, bool>::S6::S7<29, int>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S8 -
S9 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

R44

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R44
-    : S0<30, bool>::S5<31, bool>::S6::S7<32, bool>;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
S8 -
S9 -
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

R45

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R45
-    : S0<33, bool>::S5<34, bool>::S6::S7<35, bool>::S8;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f8 -
- - -
-
-
-

R46

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-template<
-    int I,
-    typename T>
-struct R46
-    : S0<36, bool>::S5<37, bool>::S6::S7<38, bool>::S9<I, T>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f9 -
- - -
-
-
-

R47

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R47
-    : S0<39, bool>::S5<40, bool>::S6::S7<41, bool>::S9<42, int>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f9 -
- - -
-
-
-

R48

-
-
-

Synopsis

-
-Declared in <class-template-specializations-1.cpp>
-
-
-struct R48
-    : S0<43, bool>::S5<44, bool>::S6::S7<45, bool>::S9<46, bool>;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f9 -
- - -
- -
-
-

Created with MrDocs

-
- - \ No newline at end of file diff --git a/test-files/golden-tests/class-template-specializations-1.xml b/test-files/golden-tests/class-template-specializations-1.xml deleted file mode 100644 index 6bf84eab44..0000000000 --- a/test-files/golden-tests/class-template-specializations-1.xml +++ /dev/null @@ -1,759 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test-files/golden-tests/class-template-specializations-3.adoc b/test-files/golden-tests/class-template-specializations-3.adoc deleted file mode 100644 index 68ece9f99c..0000000000 --- a/test-files/golden-tests/class-template-specializations-3.adoc +++ /dev/null @@ -1,452 +0,0 @@ -= Reference -:mrdocs: - -[#index] -== Global namespace - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-0e,`A`>> -| - -| <<#E,`E`>> -| - -|=== - -[#A-0e] -== A - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<typename T> -struct A; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-0e-B-07,`B`>> -| - -| <<#A-0e-B-00,`B<double>`>> -| - -|=== - - - -[#A-0e-B-07] -== <<#A-0e,A>>::B - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<typename U> -struct B; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-0e-B-07-C,`C`>> -| - -| <<#A-0e-B-07-D-09,`D`>> -| - -| <<#A-0e-B-07-D-0f,`D<bool>`>> -| - -|=== - - - -[#A-0e-B-07-C] -== <<#A-0e,A>>::<<#A-0e-B-07,B>>::C - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct C; ----- - - - - -[#A-0e-B-07-D-09] -== <<#A-0e,A>>::<<#A-0e-B-07,B>>::D - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<typename V> -struct D; ----- - - - - -[#A-0e-B-07-D-0f] -== <<#A-0e,A>>::<<#A-0e-B-07,B>>::D<bool> - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<> -struct <<#A-0e-B-07-D-09,D>><bool>; ----- - - - - -[#A-0e-B-00] -== <<#A-0e,A>>::B<double> - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<> -struct <<#A-0e-B-07,B>><double>; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-0e-B-00-C,`C`>> -| - -| <<#A-0e-B-00-D-09,`D`>> -| - -| <<#A-0e-B-00-D-0d,`D<bool>`>> -| - -|=== - - - -[#A-0e-B-00-C] -== <<#A-0e,A>>::<<#A-0e-B-00,B>><double>::C - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct C; ----- - - - - -[#A-0e-B-00-D-09] -== <<#A-0e,A>>::<<#A-0e-B-00,B>><double>::D - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<typename V> -struct D; ----- - - - - -[#A-0e-B-00-D-0d] -== <<#A-0e,A>>::<<#A-0e-B-00,B>><double>::D<bool> - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -template<> -struct <<#A-0e-B-00-D-09,D>><bool>; ----- - - - - -[#E] -== E - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct E; ----- - -=== Data Members -[cols=2] -|=== -| Name | Description - -| <<#E-m0,`m0`>> -| - -| <<#E-m1,`m1`>> -| - -| <<#E-m10,`m10`>> -| - -| <<#E-m11,`m11`>> -| - -| <<#E-m12,`m12`>> -| - -| <<#E-m13,`m13`>> -| - -| <<#E-m14,`m14`>> -| - -| <<#E-m2,`m2`>> -| - -| <<#E-m3,`m3`>> -| - -| <<#E-m4,`m4`>> -| - -| <<#E-m5,`m5`>> -| - -| <<#E-m6,`m6`>> -| - -| <<#E-m7,`m7`>> -| - -| <<#E-m8,`m8`>> -| - -| <<#E-m9,`m9`>> -| - -|=== - - - -[#E-m0] -== <<#E,E>>::m0 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><float>::<<#A-0e-B-00,B>><double> m0; ----- - -[#E-m1] -== <<#E,E>>::m1 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0e-B-00,B>><double> m1; ----- - -[#E-m2] -== <<#E,E>>::m2 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0c-B,B>><float> m2; ----- - -[#E-m3] -== <<#E,E>>::m3 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><unsigned int>::<<#A-0e-B-07,B>><float> m3; ----- - -[#E-m4] -== <<#E,E>>::m4 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><short>::<<#A-00-B,B>><void> m4; ----- - -[#E-m5] -== <<#E,E>>::m5 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><float>::<<#A-0e-B-00,B>><double>::<<#A-0e-B-00-C,C>> m5; ----- - -[#E-m6] -== <<#E,E>>::m6 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0e-B-00,B>><double>::<<#A-0e-B-00-C,C>> m6; ----- - -[#E-m7] -== <<#E,E>>::m7 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0c-B,B>><float>::<<#A-0c-B-C,C>> m7; ----- - -[#E-m8] -== <<#E,E>>::m8 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><unsigned int>::<<#A-0e-B-07,B>><float>::<<#A-0e-B-07-C,C>> m8; ----- - -[#E-m9] -== <<#E,E>>::m9 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><short>::<<#A-00-B,B>><void>::<<#A-00-B-C,C>> m9; ----- - -[#E-m10] -== <<#E,E>>::m10 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><float>::<<#A-0e-B-00,B>><double>::<<#A-0e-B-00-D-0d,D>><bool> m10; ----- - -[#E-m11] -== <<#E,E>>::m11 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0e-B-00,B>><double>::<<#A-0e-B-00-D-0d,D>><bool> m11; ----- - -[#E-m12] -== <<#E,E>>::m12 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><long>::<<#A-0c-B,B>><float>::<<#A-0c-B-D-0b,D>><bool> m12; ----- - -[#E-m13] -== <<#E,E>>::m13 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><unsigned int>::<<#A-0e-B-07,B>><float>::<<#A-0e-B-07-D-0f,D>><bool> m13; ----- - -[#E-m14] -== <<#E,E>>::m14 - -=== Synopsis - -Declared in `<class‐template‐specializations‐3.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -<<#A-0e,A>><short>::<<#A-00-B,B>><void>::<<#A-00-B-D-07,D>><bool> m14; ----- - - - -[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/class-template-specializations-3.html b/test-files/golden-tests/class-template-specializations-3.html deleted file mode 100644 index 217bdf3441..0000000000 --- a/test-files/golden-tests/class-template-specializations-3.html +++ /dev/null @@ -1,526 +0,0 @@ - - -Reference - - -
-

Reference

-
-
-

Global namespace

-
-

Types

- - - - - - - - - - - -
NameDescription
A -
E -
-
-
-
-

A

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<typename T>
-struct A;
-
-
-
-

Types

- - - - - - - - - - - -
NameDescription
B -
B<double> -
- - -
-
-
-

A::B

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<typename U>
-struct B;
-
-
-
-

Types

- - - - - - - - - - - - -
NameDescription
C -
D -
D<bool> -
- - -
-
-
-

A::B::C

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-struct C;
-
-
-
- - -
-
-
-

A::B::D

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<typename V>
-struct D;
-
-
-
- - -
-
-
-

A::B::D<bool>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<>
-struct D<bool>;
-
-
-
- - -
-
-
-

A::B<double>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<>
-struct B<double>;
-
-
-
-

Types

- - - - - - - - - - - - -
NameDescription
C -
D -
D<bool> -
- - -
-
-
-

A::B<double>::C

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-struct C;
-
-
-
- - -
-
-
-

A::B<double>::D

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<typename V>
-struct D;
-
-
-
- - -
-
-
-

A::B<double>::D<bool>

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-template<>
-struct D<bool>;
-
-
-
- - -
-
-
-

E

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-struct E;
-
-
-
-

Data Members

- - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
m0 -
m1 -
m10 -
m11 -
m12 -
m13 -
m14 -
m2 -
m3 -
m4 -
m5 -
m6 -
m7 -
m8 -
m9 -
- - -
-
-
-

E::m0

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<float>::B<double> m0;
-
-
-
-
-
-
-

E::m1

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<double> m1;
-
-
-
-
-
-
-

E::m2

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<float> m2;
-
-
-
-
-
-
-

E::m3

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<unsigned int>::B<float> m3;
-
-
-
-
-
-
-

E::m4

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<short>::B<void> m4;
-
-
-
-
-
-
-

E::m5

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<float>::B<double>::C m5;
-
-
-
-
-
-
-

E::m6

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<double>::C m6;
-
-
-
-
-
-
-

E::m7

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<float>::C m7;
-
-
-
-
-
-
-

E::m8

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<unsigned int>::B<float>::C m8;
-
-
-
-
-
-
-

E::m9

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<short>::B<void>::C m9;
-
-
-
-
-
-
-

E::m10

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<float>::B<double>::D<bool> m10;
-
-
-
-
-
-
-

E::m11

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<double>::D<bool> m11;
-
-
-
-
-
-
-

E::m12

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<long>::B<float>::D<bool> m12;
-
-
-
-
-
-
-

E::m13

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<unsigned int>::B<float>::D<bool> m13;
-
-
-
-
-
-
-

E::m14

-
-
-

Synopsis

-
-Declared in <class-template-specializations-3.cpp>
-
-
-A<short>::B<void>::D<bool> m14;
-
-
-
-
- -
-
-

Created with MrDocs

-
- - \ No newline at end of file diff --git a/test-files/golden-tests/class-template-specializations-3.xml b/test-files/golden-tests/class-template-specializations-3.xml deleted file mode 100644 index 21c7d60a40..0000000000 --- a/test-files/golden-tests/class-template-specializations-3.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test-files/golden-tests/libcxx.adoc b/test-files/golden-tests/core/libcxx.adoc similarity index 90% rename from test-files/golden-tests/libcxx.adoc rename to test-files/golden-tests/core/libcxx.adoc index 990b8b68a6..43d9d2a840 100644 --- a/test-files/golden-tests/libcxx.adoc +++ b/test-files/golden-tests/core/libcxx.adoc @@ -4,6 +4,15 @@ [#index] == Global namespace +=== Namespaces +[cols=2] +|=== +| Name | Description + +| <<#std,`std`>> +| + +|=== === Functions [cols=2] |=== @@ -16,6 +25,10 @@ |=== +[#std] +== std + + [#sqrt] == sqrt @@ -30,7 +43,7 @@ Declared in `<libcxx.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- template<typename T> -std::T +<<#std,std>>::T sqrt(T value); ---- diff --git a/test-files/golden-tests/libcxx.cpp b/test-files/golden-tests/core/libcxx.cpp similarity index 100% rename from test-files/golden-tests/libcxx.cpp rename to test-files/golden-tests/core/libcxx.cpp diff --git a/test-files/golden-tests/libcxx.html b/test-files/golden-tests/core/libcxx.html similarity index 86% rename from test-files/golden-tests/libcxx.html rename to test-files/golden-tests/core/libcxx.html index 7bc01d05d0..55deea010a 100644 --- a/test-files/golden-tests/libcxx.html +++ b/test-files/golden-tests/core/libcxx.html @@ -9,6 +9,19 @@

Reference

Global namespace

+

Namespaces

+ + + + + + + + + + +
NameDescription
std +

Functions

@@ -27,6 +40,11 @@

Functions

+

std

+
+
+
+

sqrt

Computes the square root of an integral value.

@@ -42,7 +60,7 @@

Synopsis

 
 template<typename T>
-std::T
+std::T
 sqrt(T value);
 
 
diff --git a/test-files/golden-tests/libcxx.xml b/test-files/golden-tests/core/libcxx.xml similarity index 91% rename from test-files/golden-tests/libcxx.xml rename to test-files/golden-tests/core/libcxx.xml index 80248d41da..9f6332e65d 100644 --- a/test-files/golden-tests/libcxx.xml +++ b/test-files/golden-tests/core/libcxx.xml @@ -1,39 +1,41 @@ - - - - - - + + + + + + + + diff --git a/test-files/golden-tests/libcxx.yml b/test-files/golden-tests/core/libcxx.yml similarity index 53% rename from test-files/golden-tests/libcxx.yml rename to test-files/golden-tests/core/libcxx.yml index e7067b0574..255a15647f 100644 --- a/test-files/golden-tests/libcxx.yml +++ b/test-files/golden-tests/core/libcxx.yml @@ -1,4 +1,3 @@ source-root: . input: - include: - - . + - . diff --git a/test-files/golden-tests/core/mrdocs.yml b/test-files/golden-tests/core/mrdocs.yml new file mode 100644 index 0000000000..63cf197efb --- /dev/null +++ b/test-files/golden-tests/core/mrdocs.yml @@ -0,0 +1 @@ +source-root: . \ No newline at end of file diff --git a/test-files/golden-tests/utf-8.adoc b/test-files/golden-tests/core/utf-8.adoc similarity index 100% rename from test-files/golden-tests/utf-8.adoc rename to test-files/golden-tests/core/utf-8.adoc diff --git a/test-files/golden-tests/utf-8.cpp b/test-files/golden-tests/core/utf-8.cpp similarity index 100% rename from test-files/golden-tests/utf-8.cpp rename to test-files/golden-tests/core/utf-8.cpp diff --git a/test-files/golden-tests/utf-8.html b/test-files/golden-tests/core/utf-8.html similarity index 100% rename from test-files/golden-tests/utf-8.html rename to test-files/golden-tests/core/utf-8.html diff --git a/test-files/golden-tests/utf-8.xml b/test-files/golden-tests/core/utf-8.xml similarity index 100% rename from test-files/golden-tests/utf-8.xml rename to test-files/golden-tests/core/utf-8.xml diff --git a/test-files/golden-tests/enum.adoc b/test-files/golden-tests/enum.adoc deleted file mode 100644 index b75e2a4679..0000000000 --- a/test-files/golden-tests/enum.adoc +++ /dev/null @@ -1,288 +0,0 @@ -= Reference -:mrdocs: - -[#index] -== Global namespace - -=== Enums -[cols=2] -|=== -| Name | Description - -| <<#E0,`E0`>> -| E0 brief. - - - -| <<#E1,`E1`>> -| - -| <<#E2,`E2`>> -| E2 brief. - - - -| <<#E3,`E3`>> -| - -|=== - -[#E0] -== E0 - -E0 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -enum E0; ----- - -=== Members - -[,cols=2] -|=== -|Name |Description -|`e0` -|e0 brief&period; - - -|`e1` -|e1 brief&period; - - -|=== - -=== Description - -E0 description. - - - -[#E0-e0] -== <<#E0,E0>>::e0 - -e0 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e0 = 1 ----- - -=== Description - -e0 description. - - - -[#E0-e1] -== <<#E0,E0>>::e1 - -e1 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e1 ----- - -=== Description - -e1 description. - - - -[#E1] -== E1 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -enum E1 : char; ----- - -=== Members - -[,cols=2] -|=== -|Name |Description -|`e2` -| -|`e3` -| -|=== - -[#E1-e2] -== <<#E1,E1>>::e2 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e2 ----- - -[#E1-e3] -== <<#E1,E1>>::e3 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e3 ----- - -[#E2] -== E2 - -E2 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -enum E2 : int; ----- - -=== Members - -[,cols=2] -|=== -|Name |Description -|`e4` -|e4 brief&period; - - -|`e5` -|e5 brief&period; - - -|=== - -=== Description - -E2 description. - - - -[#E2-e4] -== <<#E2,E2>>::e4 - -e4 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e4 ----- - -=== Description - -e4 description. - - - -[#E2-e5] -== <<#E2,E2>>::e5 - -e5 brief. - - - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e5 ----- - -=== Description - -e5 description. - - - -[#E3] -== E3 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -enum E3 : char; ----- - -=== Members - -[,cols=2] -|=== -|Name |Description -|`e6` -| -|`e7` -| -|=== - -[#E3-e6] -== <<#E3,E3>>::e6 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e6 ----- - -[#E3-e7] -== <<#E3,E3>>::e7 - -=== Synopsis - -Declared in `<enum.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -e7 ----- - - - -[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/filters/blacklist_0.yml b/test-files/golden-tests/filters/blacklist_0.yml deleted file mode 100644 index 420ef9ede3..0000000000 --- a/test-files/golden-tests/filters/blacklist_0.yml +++ /dev/null @@ -1,16 +0,0 @@ -filters: - symbols: - exclude: - - 'N0::N1_BL' - - 'N0::S0_BL' - - 'N2_BL' - - 'N2_BL::N3_BL' - - 'N2_BL::f0_BL' - - 'N3::f*' - - 'N4::N*::f0_BL' - - 'N7::N*::f*' - - 'N7::N8::g*' - - 'N10' - - 'N10::N11' - - 'N12' - - 'N12::N14' diff --git a/test-files/golden-tests/filters/blacklist_test.adoc b/test-files/golden-tests/filters/blacklist_test.adoc deleted file mode 100644 index 54311a311d..0000000000 --- a/test-files/golden-tests/filters/blacklist_test.adoc +++ /dev/null @@ -1,52 +0,0 @@ -= Reference -:mrdocs: - -[#index] -== Global namespace - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#blacklisted,`blacklisted`>> -| - -| <<#to_be_documented,`to_be_documented`>> -| - -|=== - -[#to_be_documented] -== to_be_documented - -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#to_be_documented-documented_function,`documented_function`>> -| - -|=== - -[#to_be_documented-documented_function] -== <<#to_be_documented,to_be_documented>>::documented_function - -=== Synopsis - -Declared in `<filters/blacklist_test.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -documented_function(); ----- - -[#blacklisted] -== blacklisted - - - - -[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/filters/blacklist_test.cpp b/test-files/golden-tests/filters/blacklist_test.cpp deleted file mode 100644 index 184b284300..0000000000 --- a/test-files/golden-tests/filters/blacklist_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -namespace to_be_documented { - void documented_function() {} -} - -namespace blacklisted { - void not_documented_function() {} -} diff --git a/test-files/golden-tests/filters/blacklist_test.html b/test-files/golden-tests/filters/blacklist_test.html deleted file mode 100644 index 29cc83a04a..0000000000 --- a/test-files/golden-tests/filters/blacklist_test.html +++ /dev/null @@ -1,73 +0,0 @@ - - -Reference - - -
-

Reference

-
-
-

Global namespace

-
-

Namespaces

-
- - - - - - - - - - -
NameDescription
blacklisted -
to_be_documented -
- -
-
-

to_be_documented

-
-

Functions

- - - - - - - - - - -
NameDescription
documented_function -
-
-
-
-

to_be_documented::documented_function

-
-
-

Synopsis

-
-Declared in <filters/blacklist_test.cpp>
-
-
-void
-documented_function();
-
-
-
-
-
-
-

blacklisted

-
-
- - -
-

Created with MrDocs

-
- - \ No newline at end of file diff --git a/test-files/golden-tests/filters/blacklist_test.xml b/test-files/golden-tests/filters/blacklist_test.xml deleted file mode 100644 index 2eae5f5896..0000000000 --- a/test-files/golden-tests/filters/blacklist_test.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - diff --git a/test-files/golden-tests/filters/blacklist_test.yml b/test-files/golden-tests/filters/blacklist_test.yml deleted file mode 100644 index dd2e9929b7..0000000000 --- a/test-files/golden-tests/filters/blacklist_test.yml +++ /dev/null @@ -1,6 +0,0 @@ -filters: - symbols: - exclude: - - 'blacklisted::*' - include: - - 'blacklisted::whitelisted_function' diff --git a/test-files/golden-tests/empty.adoc b/test-files/golden-tests/filters/file/exclude-self.adoc similarity index 100% rename from test-files/golden-tests/empty.adoc rename to test-files/golden-tests/filters/file/exclude-self.adoc diff --git a/test-files/golden-tests/filters/blacklist_0.cpp b/test-files/golden-tests/filters/file/exclude-self.cpp similarity index 100% rename from test-files/golden-tests/filters/blacklist_0.cpp rename to test-files/golden-tests/filters/file/exclude-self.cpp diff --git a/test-files/golden-tests/empty.html b/test-files/golden-tests/filters/file/exclude-self.html similarity index 100% rename from test-files/golden-tests/empty.html rename to test-files/golden-tests/filters/file/exclude-self.html diff --git a/test-files/golden-tests/empty.xml b/test-files/golden-tests/filters/file/exclude-self.xml similarity index 100% rename from test-files/golden-tests/empty.xml rename to test-files/golden-tests/filters/file/exclude-self.xml diff --git a/test-files/golden-tests/filters/file/exclude-self.yml b/test-files/golden-tests/filters/file/exclude-self.yml new file mode 100644 index 0000000000..aedda613a0 --- /dev/null +++ b/test-files/golden-tests/filters/file/exclude-self.yml @@ -0,0 +1,3 @@ +# Exclude everything in self.cpp +exclude: + - '.' \ No newline at end of file diff --git a/test-files/golden-tests/filters/file/include-self.adoc b/test-files/golden-tests/filters/file/include-self.adoc new file mode 100644 index 0000000000..3662fea817 --- /dev/null +++ b/test-files/golden-tests/filters/file/include-self.adoc @@ -0,0 +1,47 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Namespaces +[cols=2] +|=== +| Name | Description + +| <<#TEST,`TEST`>> +| + +|=== + +[#TEST] +== TEST + +=== Types +[cols=2] +|=== +| Name | Description + +| <<#TEST-SUCCESS,`SUCCESS`>> +| + +|=== + +[#TEST-SUCCESS] +== <<#TEST,TEST>>::SUCCESS + +=== Synopsis + +Declared in `<include‐self.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct SUCCESS; +---- + + + + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/filters/file/include-self.cpp b/test-files/golden-tests/filters/file/include-self.cpp new file mode 100644 index 0000000000..53e24bf0c7 --- /dev/null +++ b/test-files/golden-tests/filters/file/include-self.cpp @@ -0,0 +1,4 @@ +namespace TEST +{ + struct SUCCESS {}; +} diff --git a/test-files/golden-tests/temp/ct_mf_expl_outside.html b/test-files/golden-tests/filters/file/include-self.html similarity index 60% rename from test-files/golden-tests/temp/ct_mf_expl_outside.html rename to test-files/golden-tests/filters/file/include-self.html index ff27c6971b..3a8c5b95d9 100644 --- a/test-files/golden-tests/temp/ct_mf_expl_outside.html +++ b/test-files/golden-tests/filters/file/include-self.html @@ -9,7 +9,7 @@

Reference

Global namespace

-

Types

+

Namespaces

@@ -18,27 +18,16 @@

Types

-
A +TEST
-

A

-
-
-

Synopsis

-
-Declared in <temp/ct_mf_expl_outside.cpp>
-
-
-template<typename T>
-struct A;
-
-
+

TEST

-

Member Functions

+

Types

@@ -47,28 +36,27 @@

Member Functions

-
f +SUCCESS
- -
-

A::f

+

TEST::SUCCESS

Synopsis

-Declared in <temp/ct_mf_expl_outside.cpp>
+Declared in <include-self.cpp>
 
-void
-f();
+struct SUCCESS;
 
 
+ + diff --git a/test-files/golden-tests/filters/file/include-self.xml b/test-files/golden-tests/filters/file/include-self.xml new file mode 100644 index 0000000000..b693ebca79 --- /dev/null +++ b/test-files/golden-tests/filters/file/include-self.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/test-files/golden-tests/filters/file/include-self.yml b/test-files/golden-tests/filters/file/include-self.yml new file mode 100644 index 0000000000..6f6542a9d9 --- /dev/null +++ b/test-files/golden-tests/filters/file/include-self.yml @@ -0,0 +1,3 @@ +# Include everything in self.cpp +input: + - '.' \ No newline at end of file diff --git a/test-files/golden-tests/filters/file/mrdocs.yml b/test-files/golden-tests/filters/file/mrdocs.yml new file mode 100644 index 0000000000..63cf197efb --- /dev/null +++ b/test-files/golden-tests/filters/file/mrdocs.yml @@ -0,0 +1 @@ +source-root: . \ No newline at end of file diff --git a/test-files/golden-tests/filters/filters.adoc b/test-files/golden-tests/filters/filters.adoc deleted file mode 100644 index bbf17fe08d..0000000000 --- a/test-files/golden-tests/filters/filters.adoc +++ /dev/null @@ -1,546 +0,0 @@ -= Reference -:mrdocs: - -[#index] -== Global namespace - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#A,`A`>> -| - -| <<#B,`B`>> -| - -| <<#C,`C`>> -| - -| <<#D,`D`>> -| - -| <<#F,`F`>> -| - -|=== - -[#A] -== A - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#A-detail,`detail`>> -| - -| <<#A-detair,`detair`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#A-f0,`f0`>> -| - -|=== - -[#A-f0] -== <<#A,A>>::f0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f0(); ----- - -[#A-detail] -== <<#A,A>>::detail - -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#A-detail-g0,`g0`>> -| - -|=== - -[#A-detail-g0] -== <<#A,A>>::<<#A-detail,detail>>::g0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g0(); ----- - -[#A-detair] -== <<#A,A>>::detair - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-detair-Y,`Y`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#A-detair-f4,`f4`>> -| - -|=== - -[#A-detair-f4] -== <<#A,A>>::<<#A-detair,detair>>::f4 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f4(); ----- - -[#A-detair-Y] -== <<#A,A>>::<<#A-detair,detair>>::Y - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct Y; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#A-detair-Y-Z,`Z`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#A-detair-Y-f6,`f6`>> -| - -|=== - - - -[#A-detair-Y-f6] -== <<#A,A>>::<<#A-detair,detair>>::<<#A-detair-Y,Y>>::f6 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f6(); ----- - -[#A-detair-Y-Z] -== <<#A,A>>::<<#A-detair,detair>>::<<#A-detair-Y,Y>>::Z - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct Z; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#A-detair-Y-Z-f7,`f7`>> -| - -|=== - - - -[#A-detair-Y-Z-f7] -== <<#A,A>>::<<#A-detair,detair>>::<<#A-detair-Y,Y>>::<<#A-detair-Y-Z,Z>>::f7 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f7(); ----- - -[#B] -== B - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#B-detair,`detair`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#B-f0,`f0`>> -| - -|=== - -[#B-f0] -== <<#B,B>>::f0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f0(); ----- - -[#B-detair] -== <<#B,B>>::detair - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#B-detair-Y,`Y`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#B-detair-f4,`f4`>> -| - -|=== - -[#B-detair-f4] -== <<#B,B>>::<<#B-detair,detair>>::f4 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f4(); ----- - -[#B-detair-Y] -== <<#B,B>>::<<#B-detair,detair>>::Y - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct Y; ----- - -=== Types -[cols=2] -|=== -| Name | Description - -| <<#B-detair-Y-Z,`Z`>> -| - -|=== -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#B-detair-Y-f6,`f6`>> -| - -|=== - - - -[#B-detair-Y-f6] -== <<#B,B>>::<<#B-detair,detair>>::<<#B-detair-Y,Y>>::f6 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f6(); ----- - -[#B-detair-Y-Z] -== <<#B,B>>::<<#B-detair,detair>>::<<#B-detair-Y,Y>>::Z - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -struct Z; ----- - -=== Member Functions -[cols=2] -|=== -| Name | Description - -| <<#B-detair-Y-Z-f7,`f7`>> -| - -|=== - - - -[#B-detair-Y-Z-f7] -== <<#B,B>>::<<#B-detair,detair>>::<<#B-detair-Y,Y>>::<<#B-detair-Y-Z,Z>>::f7 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f7(); ----- - -[#C] -== C - -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#C-g0,`g0`>> -| - -|=== - -[#C-g0] -== <<#C,C>>::g0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g0(); ----- - -[#D] -== D - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#D-E,`E`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#D-f1,`f1`>> -| - -| <<#D-g1,`g1`>> -| - -|=== - -[#D-E] -== <<#D,D>>::E - -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#D-E-g0,`g0`>> -| - -|=== - -[#D-E-g0] -== <<#D,D>>::<<#D-E,E>>::g0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g0(); ----- - -[#D-f1] -== <<#D,D>>::f1 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f1(); ----- - -[#D-g1] -== <<#D,D>>::g1 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g1(); ----- - -[#F] -== F - -=== Namespaces -[cols=2] -|=== -| Name | Description - -| <<#F-G,`G`>> -| - -|=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#F-g0,`g0`>> -| - -|=== - -[#F-g0] -== <<#F,F>>::g0 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g0(); ----- - -[#F-G] -== <<#F,F>>::G - -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#F-G-f1,`f1`>> -| - -| <<#F-G-g1,`g1`>> -| - -|=== - -[#F-G-f1] -== <<#F,F>>::<<#F-G,G>>::f1 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f1(); ----- - -[#F-G-g1] -== <<#F,F>>::<<#F-G,G>>::g1 - -=== Synopsis - -Declared in `<filters/filters.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -g1(); ----- - - - -[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/filters/filters.cpp b/test-files/golden-tests/filters/filters.cpp deleted file mode 100644 index 97812a225d..0000000000 --- a/test-files/golden-tests/filters/filters.cpp +++ /dev/null @@ -1,126 +0,0 @@ -namespace A -{ - void f0(); - - namespace detail - { - void f1(); - void g0(); - - struct X - { - void f2(); - }; - - struct Y - { - void f3(); - - struct Z - { - void f8(); - }; - }; - } - - namespace detair - { - void f4(); - - struct X - { - void f5(); - }; - - struct Y - { - void f6(); - - struct Z - { - void f7(); - }; - }; - } -} - -namespace B -{ - void f0(); - - namespace detail - { - void f1(); - void g0(); - - struct X - { - void f2(); - }; - - struct Y - { - void f3(); - - struct Z - { - void f8(); - }; - }; - } - - namespace detair - { - void f4(); - - struct X - { - void f5(); - }; - - struct Y - { - void f6(); - - struct Z - { - void f7(); - }; - }; - } -} - - -namespace C -{ - void f0(); - void g0(); -} - -namespace D -{ - namespace E - { - void f0(); - void g0(); - } - void f1(); - void g1(); -} - - -void D::E::f0(); -void D::E::g0(); - - -namespace F -{ - void f0(); - void g0(); - - namespace G - { - void f1(); - void g1(); - } -} diff --git a/test-files/golden-tests/filters/filters.html b/test-files/golden-tests/filters/filters.html deleted file mode 100644 index 5358e29812..0000000000 --- a/test-files/golden-tests/filters/filters.html +++ /dev/null @@ -1,706 +0,0 @@ - - -Reference - - -
-

Reference

-
-
-

Global namespace

-
-

Namespaces

- - - - - - - - - - - - - - -
NameDescription
A -
B -
C -
D -
F -
-
-
-
-

A

-
-

Namespaces

- - - - - - - - - - - -
NameDescription
detail -
detair -
-

Functions

- - - - - - - - - - -
NameDescription
f0 -
-
-
-
-

A::f0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f0();
-
-
-
-
-
-
-

A::detail

-
-

Functions

- - - - - - - - - - -
NameDescription
g0 -
-
-
-
-

A::detail::g0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g0();
-
-
-
-
-
-
-

A::detair

-
-

Types

- - - - - - - - - - -
NameDescription
Y -
-

Functions

- - - - - - - - - - -
NameDescription
f4 -
-
-
-
-

A::detair::f4

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f4();
-
-
-
-
-
-
-

A::detair::Y

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-struct Y;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
Z -
-

Member Functions

- - - - - - - - - - -
NameDescription
f6 -
- - -
-
-
-

A::detair::Y::f6

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f6();
-
-
-
-
-
-
-

A::detair::Y::Z

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-struct Z;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

A::detair::Y::Z::f7

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f7();
-
-
-
-
-
-
-

B

-
-

Namespaces

- - - - - - - - - - -
NameDescription
detair -
-

Functions

- - - - - - - - - - -
NameDescription
f0 -
-
-
-
-

B::f0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f0();
-
-
-
-
-
-
-

B::detair

-
-

Types

- - - - - - - - - - -
NameDescription
Y -
-

Functions

- - - - - - - - - - -
NameDescription
f4 -
-
-
-
-

B::detair::f4

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f4();
-
-
-
-
-
-
-

B::detair::Y

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-struct Y;
-
-
-
-

Types

- - - - - - - - - - -
NameDescription
Z -
-

Member Functions

- - - - - - - - - - -
NameDescription
f6 -
- - -
-
-
-

B::detair::Y::f6

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f6();
-
-
-
-
-
-
-

B::detair::Y::Z

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-struct Z;
-
-
-
-

Member Functions

- - - - - - - - - - -
NameDescription
f7 -
- - -
-
-
-

B::detair::Y::Z::f7

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f7();
-
-
-
-
-
-
-

C

-
-

Functions

- - - - - - - - - - -
NameDescription
g0 -
-
-
-
-

C::g0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g0();
-
-
-
-
-
-
-

D

-
-

Namespaces

- - - - - - - - - - -
NameDescription
E -
-

Functions

- - - - - - - - - - - -
NameDescription
f1 -
g1 -
-
-
-
-

D::E

-
-

Functions

- - - - - - - - - - -
NameDescription
g0 -
-
-
-
-

D::E::g0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g0();
-
-
-
-
-
-
-

D::f1

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f1();
-
-
-
-
-
-
-

D::g1

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g1();
-
-
-
-
-
-
-

F

-
-

Namespaces

- - - - - - - - - - -
NameDescription
G -
-

Functions

- - - - - - - - - - -
NameDescription
g0 -
-
-
-
-

F::g0

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g0();
-
-
-
-
-
-
-

F::G

-
-

Functions

- - - - - - - - - - - -
NameDescription
f1 -
g1 -
-
-
-
-

F::G::f1

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-f1();
-
-
-
-
-
-
-

F::G::g1

-
-
-

Synopsis

-
-Declared in <filters/filters.cpp>
-
-
-void
-g1();
-
-
-
-
- -
-
-

Created with MrDocs

-
- - \ No newline at end of file diff --git a/test-files/golden-tests/filters/filters.xml b/test-files/golden-tests/filters/filters.xml deleted file mode 100644 index c5ecd233ad..0000000000 --- a/test-files/golden-tests/filters/filters.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test-files/golden-tests/filters/filters.yml b/test-files/golden-tests/filters/filters.yml deleted file mode 100644 index 782baefc9d..0000000000 --- a/test-files/golden-tests/filters/filters.yml +++ /dev/null @@ -1,21 +0,0 @@ -filters: - symbols: - exclude: - - '*::detail' - - '*::detai*::X' - - '*::detail::Y' - - '*::detail::Y::Z' - - 'C' - - 'C::f0' - - 'D::E::f0' - - 'F::f0' - # - 'A::detail' - # - '*::detail::Z' - # - 'A::detail::z0' - # - 'A::f1' - # - '*::detail::Z' - include: - - 'F' - - 'A::detail::g0' - - 'C::g0' - - 'D::E::g0' diff --git a/test-files/golden-tests/filters/blacklist_0.adoc b/test-files/golden-tests/filters/symbol-name/blacklist_0.adoc similarity index 85% rename from test-files/golden-tests/filters/blacklist_0.adoc rename to test-files/golden-tests/filters/symbol-name/blacklist_0.adoc index 0bb5d80ece..da13ae7e50 100644 --- a/test-files/golden-tests/filters/blacklist_0.adoc +++ b/test-files/golden-tests/filters/symbol-name/blacklist_0.adoc @@ -41,7 +41,7 @@ === Synopsis -Declared in `<filters/blacklist_0.cpp>` +Declared in `<blacklist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- @@ -87,7 +87,7 @@ f0(); === Synopsis -Declared in `<filters/blacklist_0.cpp>` +Declared in `<blacklist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- @@ -113,7 +113,7 @@ f1(); === Synopsis -Declared in `<filters/blacklist_0.cpp>` +Declared in `<blacklist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- @@ -159,7 +159,7 @@ f1(); === Synopsis -Declared in `<filters/blacklist_0.cpp>` +Declared in `<blacklist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- diff --git a/test-files/golden-tests/filters/symbol-name/blacklist_0.cpp b/test-files/golden-tests/filters/symbol-name/blacklist_0.cpp new file mode 100644 index 0000000000..353ac8c966 --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/blacklist_0.cpp @@ -0,0 +1,88 @@ +namespace N0 +{ + void f0(); + + namespace N1_BL + { + void f1(); + } + + struct S0_BL + { + void f2(); + }; +} + +void N0::f0(); +void N0::N1_BL::f1(); +void N0::S0_BL::f2() { } + +namespace N2_BL +{ + void f0_BL(); + + namespace N3_BL + { + void f1(); + } + + void f2(); +} + +namespace N3 +{ + void f0_BL(); + void f1_BL(); +} + +namespace N4 +{ + namespace N5 + { + void f0_BL(); + void f1(); + } + + namespace N6 + { + void f0_BL(); + void f1(); + } +} + +namespace N7 +{ + namespace N8 + { + void f0_BL(); + void g0_BL(); + } + + namespace N9 + { + void f0_BL(); + void g0(); + } +} + +namespace N10 +{ + void f0_BL(); + namespace N11 + { + void f1_BL(); + } +} + +namespace N12 +{ + void f0_BL(); + namespace N13 + { + void f1_BL(); + namespace N14 + { + void f2_BL(); + } + } +} diff --git a/test-files/golden-tests/filters/blacklist_0.html b/test-files/golden-tests/filters/symbol-name/blacklist_0.html similarity index 93% rename from test-files/golden-tests/filters/blacklist_0.html rename to test-files/golden-tests/filters/symbol-name/blacklist_0.html index e7ee0fbc42..c53ad2fb78 100644 --- a/test-files/golden-tests/filters/blacklist_0.html +++ b/test-files/golden-tests/filters/symbol-name/blacklist_0.html @@ -54,7 +54,7 @@

N0::f0

Synopsis

-Declared in <filters/blacklist_0.cpp>
+Declared in <blacklist_0.cpp>
 
 void
@@ -113,7 +113,7 @@ 

N4::N5::f1

Synopsis

-Declared in <filters/blacklist_0.cpp>
+Declared in <blacklist_0.cpp>
 
 void
@@ -147,7 +147,7 @@ 

N4::N6::f1

Synopsis

-Declared in <filters/blacklist_0.cpp>
+Declared in <blacklist_0.cpp>
 
 void
@@ -206,7 +206,7 @@ 

N7::N9::g0

Synopsis

-Declared in <filters/blacklist_0.cpp>
+Declared in <blacklist_0.cpp>
 
 void
diff --git a/test-files/golden-tests/filters/blacklist_0.xml b/test-files/golden-tests/filters/symbol-name/blacklist_0.xml
similarity index 62%
rename from test-files/golden-tests/filters/blacklist_0.xml
rename to test-files/golden-tests/filters/symbol-name/blacklist_0.xml
index 1ab19efafd..956874f3e2 100644
--- a/test-files/golden-tests/filters/blacklist_0.xml
+++ b/test-files/golden-tests/filters/symbol-name/blacklist_0.xml
@@ -4,21 +4,32 @@
 
   
     
-      
-      
+      
+      
     
+    
+      
+        
+      
+    
+    
+      
+      
+        
+      
+    
   
   
   
   
     
       
-        
+        
       
     
     
       
-        
+        
       
     
   
@@ -27,7 +38,7 @@
     
     
       
-        
+        
       
     
   
diff --git a/test-files/golden-tests/filters/symbol-name/blacklist_0.yml b/test-files/golden-tests/filters/symbol-name/blacklist_0.yml
new file mode 100644
index 0000000000..0f40b1b799
--- /dev/null
+++ b/test-files/golden-tests/filters/symbol-name/blacklist_0.yml
@@ -0,0 +1,14 @@
+exclude-symbols:
+  - 'N0::N1_BL'
+  - 'N0::S0_BL'
+  - 'N2_BL'
+  - 'N2_BL::N3_BL'
+  - 'N2_BL::f0_BL'
+  - 'N3::f*'
+  - 'N4::N*::f0_BL'
+  - 'N7::N*::f*'
+  - 'N7::N8::g*'
+  - 'N10'
+  - 'N10::N11'
+  - 'N12'
+  - 'N12::N14'
diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.adoc b/test-files/golden-tests/filters/symbol-name/extraction-mode.adoc
new file mode 100644
index 0000000000..da021e6863
--- /dev/null
+++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.adoc
@@ -0,0 +1,780 @@
+= Reference
+:mrdocs:
+
+[#index]
+== Global namespace
+
+=== Namespaces
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular_ns,`regular_ns`>> 
+| A regular namespace with different filters for members
+
+
+
+| <<#see_below_ns,`see_below_ns`>> 
+| A see‐below namespace
+
+
+
+|===
+=== Namespace Aliases
+[cols=2]
+|===
+| Name | Description 
+
+| <<#dependency_ns_alias,`dependency_ns_alias`>> 
+| Namespace alias to form the dependency on dependency_ns
+
+
+
+| <<#implementation_defined_ns_alias,`implementation_defined_ns_alias`>> 
+| Namespace alias to form a dependency on the implementation‐defined namespace
+
+
+
+| <<#see_below_ns_alias,`see_below_ns_alias`>> 
+| Namespace alias to form a dependency on the see‐below namespace
+
+
+
+|===
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular,`regular`>> 
+| A regular symbol in the global namespace
+
+
+
+| <<#see_below,`see_below`>> 
+| A see‐below symbol in the global namespace
+
+
+
+|===
+=== Functions
+[cols=2]
+|===
+| Name | Description 
+
+| <<#get_dependency,`get_dependency`>> 
+| A function to get a dependency symbol on the global namespace
+
+
+
+| <<#get_implementation_defined,`get_implementation_defined`>> 
+| A function to get an implementation‐defined symbol in the global namespace
+
+
+
+| <<#get_regular,`get_regular`>> 
+| A function to get a regular symbol in the global namespace
+
+
+
+| <<#get_see_below,`get_see_below`>> 
+| A function to get a see‐below symbol in the global namespace
+
+
+
+|===
+
+[#regular_ns]
+== regular_ns
+
+A regular namespace with different filters for members
+
+
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular_ns-regular,`regular`>> 
+| A symbol that passes the filters
+
+
+
+| <<#regular_ns-see_below,`see_below`>> 
+| A symbol that passes the see‐below filter
+
+
+
+|===
+=== Functions
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular_ns-get_dependency,`get_dependency`>> 
+| A function to get an excluded symbol
+
+
+
+| <<#regular_ns-get_implementation_defined,`get_implementation_defined`>> 
+| A function to get an implementation‐defined symbol
+
+
+
+| <<#regular_ns-get_regular,`get_regular`>> 
+| A function to get a regular symbol
+
+
+
+| <<#regular_ns-get_see_below,`get_see_below`>> 
+| A function to get a see‐below symbol
+
+
+
+|===
+
+[#regular_ns-regular]
+== <<#regular_ns,regular_ns>>::regular
+
+A symbol that passes the filters
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct regular;
+----
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular_ns-regular-also_regular,`also_regular`>> 
+| Child of a regular symbol extracted as regular
+
+
+
+|===
+
+
+
+=== Description
+
+The symbol should have a page as usual
+
+
+
+[#regular_ns-regular-also_regular]
+== <<#regular_ns,regular_ns>>::<<#regular_ns-regular,regular>>::also_regular
+
+Child of a regular symbol extracted as regular
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct also_regular;
+----
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular_ns-regular-also_regular-regular_as_well,`regular_as_well`>> 
+| Grandchild of a regular symbol extracted as regular
+
+
+
+|===
+
+
+
+[#regular_ns-regular-also_regular-regular_as_well]
+== <<#regular_ns,regular_ns>>::<<#regular_ns-regular,regular>>::<<#regular_ns-regular-also_regular,also_regular>>::regular_as_well
+
+Grandchild of a regular symbol extracted as regular
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct regular_as_well;
+----
+
+
+
+
+[#regular_ns-see_below]
+== <<#regular_ns,regular_ns>>::see_below
+
+A symbol that passes the see‐below filter
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct see_below { /* see-below */ };
+----
+
+
+
+
+=== Description
+
+A symbol that passes the filters and the see‐below filter.
+The symbol should have a page as usual but, because it's a scope
+and not a namespace, the members should not be listed on that page.
+
+
+
+[#regular_ns-get_dependency]
+== <<#regular_ns,regular_ns>>::get_dependency
+
+A function to get an excluded symbol
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+dependency
+get_dependency();
+----
+
+=== Description
+
+When used in a function, only the symbol name should be shown.
+No links should be generated for this symbol.
+
+
+
+[#regular_ns-get_implementation_defined]
+== <<#regular_ns,regular_ns>>::get_implementation_defined
+
+A function to get an implementation‐defined symbol
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+/* implementation-defined */
+get_implementation_defined();
+----
+
+=== Description
+
+When used in a function, the implementation‐defined
+comment should replace the real type.
+
+It's the responsibility of the function documentation
+to explain the implementation‐defined symbol.
+
+
+
+[#regular_ns-get_regular]
+== <<#regular_ns,regular_ns>>::get_regular
+
+A function to get a regular symbol
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+<<#regular_ns-regular,regular>>
+get_regular();
+----
+
+=== Description
+
+When used in a function, the symbol should be shown as usual
+with a link to the page.
+
+
+
+[#regular_ns-get_see_below]
+== <<#regular_ns,regular_ns>>::get_see_below
+
+A function to get a see‐below symbol
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+<<#regular_ns-see_below,see_below>>
+get_see_below();
+----
+
+=== Description
+
+When used in a function, the symbol name should be shown as usual.
+The page for this symbol is what should be different because
+the synopsis should say "See below" and the members are not
+listed unless it's a namespace or the symbol has been explicitly
+used as a dependency elsewhere.
+
+
+
+[#see_below_ns]
+== see_below_ns
+
+A see‐below namespace
+
+
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#see_below_ns-regular,`regular`>> 
+| Regular symbol in a see‐below namespace
+
+
+
+| <<#see_below_ns-see_below,`see_below`>> 
+| See‐below symbol in a see‐below namespace
+
+
+
+|===
+=== Functions
+[cols=2]
+|===
+| Name | Description 
+
+| <<#see_below_ns-get_dependency,`get_dependency`>> 
+| A function to get a dependency symbol in a see‐below namespace
+
+
+
+| <<#see_below_ns-get_implementation_defined,`get_implementation_defined`>> 
+| A function to get an implementation‐defined symbol in a see‐below namespace
+
+
+
+|===
+
+=== Description
+
+All member symbols should become see‐below. All members are
+traversed as see‐below.
+
+The documentation page for these symbols should include
+the see‐below comment.
+
+
+
+[#see_below_ns-regular]
+== <<#see_below_ns,see_below_ns>>::regular
+
+Regular symbol in a see‐below namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct regular { /* see-below */ };
+----
+
+
+
+
+=== Description
+
+The symbol becomes see‐below because the whole namespace
+is see‐below.
+
+
+
+[#see_below_ns-see_below]
+== <<#see_below_ns,see_below_ns>>::see_below
+
+See‐below symbol in a see‐below namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct see_below { /* see-below */ };
+----
+
+
+
+
+=== Description
+
+The symbol becomes see‐below because the whole namespace
+is see‐below and because it's explicitly marked as see‐below.
+
+
+
+[#see_below_ns-get_dependency]
+== <<#see_below_ns,see_below_ns>>::get_dependency
+
+A function to get a dependency symbol in a see‐below namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+dependency
+get_dependency();
+----
+
+=== Description
+
+The symbol should be extracted as a dependency because the
+exclude filter has precedence over the see‐below filter.
+Only included symbols can be promoted to see‐below.
+
+It's the responsibility of the function documentation
+to explain the dependency.
+
+
+
+[#see_below_ns-get_implementation_defined]
+== <<#see_below_ns,see_below_ns>>::get_implementation_defined
+
+A function to get an implementation‐defined symbol in a see‐below namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+/* implementation-defined */
+get_implementation_defined();
+----
+
+=== Description
+
+When used in a function, the implementation‐defined
+comment should replace the real type.
+
+It's the responsibility of the function documentation
+to explain the implementation‐defined symbol.
+
+
+
+[#regular]
+== regular
+
+A regular symbol in the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct regular;
+----
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular-also_regular,`also_regular`>> 
+| Child of a regular symbol: should be traversed as usual
+
+
+
+|===
+
+
+
+=== Description
+
+This symbol should have a page as usual.
+
+
+
+[#regular-also_regular]
+== <<#regular,regular>>::also_regular
+
+Child of a regular symbol: should be traversed as usual
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct also_regular;
+----
+
+=== Types
+[cols=2]
+|===
+| Name | Description 
+
+| <<#regular-also_regular-regular_as_well,`regular_as_well`>> 
+| Grandchild of a regular symbol: should be traversed as usual
+
+
+
+|===
+
+
+
+[#regular-also_regular-regular_as_well]
+== <<#regular,regular>>::<<#regular-also_regular,also_regular>>::regular_as_well
+
+Grandchild of a regular symbol: should be traversed as usual
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct regular_as_well;
+----
+
+
+
+
+[#see_below]
+== see_below
+
+A see‐below symbol in the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+struct see_below { /* see-below */ };
+----
+
+
+
+
+=== Description
+
+This symbol should have a page as usual but, because it's a scope
+and not a namespace, the members should not be listed on that page.
+
+The synopsis should say "See below".
+
+
+
+[#get_dependency]
+== get_dependency
+
+A function to get a dependency symbol on the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+dependency
+get_dependency();
+----
+
+=== Description
+
+The symbol should be extracted as a dependency but its
+members should not be traversed.
+
+
+
+[#get_implementation_defined]
+== get_implementation_defined
+
+A function to get an implementation‐defined symbol in the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+/* implementation-defined */
+get_implementation_defined();
+----
+
+=== Description
+
+When used in a function, the implementation‐defined
+comment should replace the real type.
+
+It's the responsibility of the function documentation
+to explain the implementation‐defined symbol.
+
+
+
+[#get_regular]
+== get_regular
+
+A function to get a regular symbol in the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+<<#regular,regular>>
+get_regular();
+----
+
+=== Description
+
+When used in a function, the symbol should be shown as usual
+with a link to the page.
+
+
+
+[#get_see_below]
+== get_see_below
+
+A function to get a see‐below symbol in the global namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+<<#see_below,see_below>>
+get_see_below();
+----
+
+=== Description
+
+When used in a function, the symbol name should be shown as usual.
+The page for this symbol is what should be different because
+the synopsis should say "See below" and the members are not
+listed unless it's a namespace or the symbol has been explicitly
+used as a dependency elsewhere.
+
+
+
+[#dependency_ns_alias]
+== dependency_ns_alias
+
+Namespace alias to form the dependency on dependency_ns
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+namespace dependency_ns_alias = dependency_ns;
+----
+
+[#implementation_defined_ns_alias]
+== implementation_defined_ns_alias
+
+Namespace alias to form a dependency on the implementation‐defined namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+namespace implementation_defined_ns_alias = /* implementation-defined */;
+----
+
+[#see_below_ns_alias]
+== see_below_ns_alias
+
+Namespace alias to form a dependency on the see‐below namespace
+
+
+
+=== Synopsis
+
+Declared in `<extraction‐mode.cpp>`
+
+[source,cpp,subs="verbatim,replacements,macros,-callouts"]
+----
+namespace see_below_ns_alias = <<#see_below_ns,see_below_ns>>;
+----
+
+=== Description
+
+The alias should be linked as usual and, because it's a namespace,
+the members should be listed on the page.
+
+
+
+
+
+[.small]#Created with https://www.mrdocs.com[MrDocs]#
diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.cpp b/test-files/golden-tests/filters/symbol-name/extraction-mode.cpp
new file mode 100644
index 0000000000..4793d51956
--- /dev/null
+++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.cpp
@@ -0,0 +1,316 @@
+/*
+    The test explores all combinations of filters and extraction modes:
+
+    * Global namespace that includes symbols with different filters
+    * Namespaces that pass each of the filters
+    * Each namespace has a symbol that passes each of the filters
+
+ */
+
+/// A regular namespace with different filters for members
+namespace regular_ns
+{
+    /// A symbol that passes the filters
+    ///
+    /// The symbol should have a page as usual
+    struct regular {
+        /// Child of a regular symbol extracted as regular
+        struct also_regular {
+            /// Grandchild of a regular symbol extracted as regular
+            struct regular_as_well {};
+        };
+    };
+
+    /// A function to get a regular symbol
+    ///
+    /// When used in a function, the symbol should be shown as usual
+    /// with a link to the page.
+    regular get_regular() { return {}; }
+
+    /// A symbol that passes the see-below filter
+    ///
+    /// A symbol that passes the filters and the see-below filter.
+    /// The symbol should have a page as usual but, because it's a scope
+    /// and not a namespace, the members should not be listed on that page.
+    struct see_below {
+        /// Child of a see-below struct: should not be traversed
+        struct also_see_below {
+            /// Grandchild of a see-below struct: should not be traversed
+            struct hidden_from_page {};
+        };
+    };
+
+    /// A function to get a see-below symbol
+    ///
+    /// When used in a function, the symbol name should be shown as usual.
+    /// The page for this symbol is what should be different because
+    /// the synopsis should say "See below" and the members are not
+    /// listed unless it's a namespace or the symbol has been explicitly
+    /// used as a dependency elsewhere.
+    see_below get_see_below() { return {}; }
+
+    /// A symbol that passes the implementation-defined filter
+    ///
+    /// A symbol that passes the filters and the implementation-defined filter
+    /// The symbol is implementation defined and should not have a page.
+    /// Members of an implementation-defined scope should not be traversed.
+    /// If they are traversed for some other reason, they should also become
+    /// implementation-defined.
+    struct implementation_defined {
+        /// Child of an implementation-defined struct
+        ///
+        /// Child of an implementation-defined struct: should not be traversed
+        /// and, if traversed for some other reason, should also be
+        /// implementation-defined.
+        struct also_implementation_defined {
+            /// Grandchild of an implementation-defined struct
+            ///
+            /// Grandchild of an implementation-defined struct: should not be
+            /// traversed and, if traversed for some other reason, should also
+            /// be implementation-defined.
+            struct hidden_from_page {};
+        };
+    };
+
+    /// A function to get an implementation-defined symbol
+    ///
+    /// When used in a function, the implementation-defined
+    /// comment should replace the real type.
+    ///
+    /// It's the responsibility of the function documentation
+    /// to explain the implementation-defined symbol.
+    implementation_defined get_implementation_defined() { return {}; }
+
+    /// An excluded symbol used as a dependency by a regular symbol
+    ///
+    /// A symbol excluded by filters but is used as a dependency
+    /// The symbol should be extracted as a dependency but its
+    /// members should not be traversed.
+    struct dependency {
+        /// Child of a dependency struct
+        ///
+        /// Child of a dependency struct: should not be traversed
+        /// and, if traversed for some other reason, should also be
+        /// a dependency.
+        struct hidden_from_page;
+    };
+
+    /// A function to get an excluded symbol
+    ///
+    /// When used in a function, only the symbol name should be shown.
+    /// No links should be generated for this symbol.
+    dependency get_dependency() { return {}; }
+
+    /// A symbol excluded by filters
+    ///
+    /// A symbol excluded by filters and should not have a page.
+    struct excluded {
+        struct also_excluded {
+            struct hidden_from_page {};
+        };
+    };
+}
+
+/// A see-below namespace
+///
+/// All member symbols should become see-below. All members are
+/// traversed as see-below.
+///
+/// The documentation page for these symbols should include
+/// the see-below comment.
+namespace see_below_ns
+{
+    /// Regular symbol in a see-below namespace
+    ///
+    /// The symbol becomes see-below because the whole namespace
+    /// is see-below.
+    struct regular {
+        /// Child of a regular symbol in a see-below namespace
+        ///
+        /// This symbol should not have a page because regular
+        /// became see-below.
+        struct also_regular {
+            /// Grandchild of a regular symbol in a see-below namespace
+            ///
+            /// This symbol should not have a page because regular
+            /// became see-below.
+            struct regular_as_well {};
+        };
+    };
+
+    /// See-below symbol in a see-below namespace
+    ///
+    /// The symbol becomes see-below because the whole namespace
+    /// is see-below and because it's explicitly marked as see-below.
+    struct see_below {};
+
+    /// Implementation-defined symbol in a see-below namespace
+    ///
+    /// The symbol does not become see-below because the
+    /// the implentation-defined filter has precedence over the
+    /// see-below filter.
+    ///
+    /// Functions using this symbol should explain the implementation-defined
+    /// nature of the symbol.
+    struct implementation_defined {};
+
+    /// A function to get an implementation-defined symbol in a see-below namespace
+    ///
+    /// When used in a function, the implementation-defined
+    /// comment should replace the real type.
+    ///
+    /// It's the responsibility of the function documentation
+    /// to explain the implementation-defined symbol.
+    implementation_defined get_implementation_defined() { return {}; }
+
+    /// A dependency symbol in a see-below namespace
+    ///
+    /// The symbol should be extracted as a dependency because the
+    /// exclude filter has precedence over the see-below filter.
+    /// Only included symbols can be promoted to see-below.
+    ///
+    /// This will not have a page and functions using this symbol
+    /// should explain the dependency.
+    struct dependency {};
+
+    /// A function to get a dependency symbol in a see-below namespace
+    ///
+    /// The symbol should be extracted as a dependency because the
+    /// exclude filter has precedence over the see-below filter.
+    /// Only included symbols can be promoted to see-below.
+    ///
+    /// It's the responsibility of the function documentation
+    /// to explain the dependency.
+    dependency get_dependency() { return {}; }
+
+    /// A symbol excluded by filters in a see-below namespace
+    ///
+    /// The symbol is excluded by filters and should not have a page.
+    struct excluded {};
+}
+
+/// Namespace alias to form a dependency on the see-below namespace
+///
+/// The alias should be linked as usual and, because it's a namespace,
+/// the members should be listed on the page.
+namespace see_below_ns_alias = see_below_ns;
+
+/// An implementation-defined namespace
+///
+/// Members are not traversed and, if traversed for some
+/// other reason, they also become implementation-defined.
+namespace implementation_defined_ns
+{
+    // Symbols directly filtered
+    struct regular {};
+    struct see_below {};
+    struct implementation_defined {};
+    struct dependency {};
+    struct excluded {};
+
+    dependency f() { return {}; }
+}
+
+/// Namespace alias to form a dependency on the implementation-defined namespace
+namespace implementation_defined_ns_alias = implementation_defined_ns;
+
+// A dependency namespace: all member symbols become dependencies
+// Members should not be traversed and if they are, they should
+// become dependencies.
+namespace dependency_ns
+{
+    // Symbols directly filtered
+    struct regular {};
+    struct see_below {};
+    struct implementation_defined {};
+    struct dependency {};
+    struct excluded {};
+
+    dependency f() { return {}; }
+}
+
+/// Namespace alias to form the dependency on dependency_ns
+namespace dependency_ns_alias = dependency_ns;
+
+// An excluded namespace: all member symbols are excluded.
+namespace excluded_ns
+{
+    // Symbols directly filtered
+    struct regular {};
+    struct see_below {};
+    struct implementation_defined {};
+    struct dependency {};
+    struct excluded {};
+
+    dependency f() { return {}; }
+}
+
+/// A regular symbol in the global namespace
+///
+/// This symbol should have a page as usual.
+struct regular {
+    /// Child of a regular symbol: should be traversed as usual
+    struct also_regular {
+        /// Grandchild of a regular symbol: should be traversed as usual
+        struct regular_as_well {};
+    };
+};
+
+/// A function to get a regular symbol in the global namespace
+///
+/// When used in a function, the symbol should be shown as usual
+/// with a link to the page.
+regular get_regular() { return {}; }
+
+/// A see-below symbol in the global namespace
+///
+/// This symbol should have a page as usual but, because it's a scope
+/// and not a namespace, the members should not be listed on that page.
+///
+/// The synopsis should say "See below".
+struct see_below {
+    /// Child of a see-below struct: should not be traversed
+    /// and, if traversed for some other reason, should also be see-below.
+    /// The symbol should not have a page.
+    struct also_see_below {
+        /// Grandchild of a see-below struct: should not be traversed
+        /// and, if traversed for some other reason, should also be see-below.
+        /// The symbol should not have a page.
+        struct hidden_from_page {};
+    };
+};
+
+/// A function to get a see-below symbol in the global namespace
+///
+/// When used in a function, the symbol name should be shown as usual.
+/// The page for this symbol is what should be different because
+/// the synopsis should say "See below" and the members are not
+/// listed unless it's a namespace or the symbol has been explicitly
+/// used as a dependency elsewhere.
+see_below get_see_below() { return {}; }
+
+/// An implementation-defined symbol in the global namespace
+///
+/// The symbol is implementation defined and should not have a page.
+struct implementation_defined {};
+
+/// A function to get an implementation-defined symbol in the global namespace
+///
+/// When used in a function, the implementation-defined
+/// comment should replace the real type.
+///
+/// It's the responsibility of the function documentation
+/// to explain the implementation-defined symbol.
+implementation_defined get_implementation_defined() { return {}; }
+
+/// A dependency symbol in the global namespace
+struct dependency {};
+
+/// A function to get a dependency symbol on the global namespace
+///
+/// The symbol should be extracted as a dependency but its
+/// members should not be traversed.
+dependency get_dependency() { return {}; }
+
+/// An excluded symbol in the global namespace
+struct excluded {};
diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.html b/test-files/golden-tests/filters/symbol-name/extraction-mode.html
new file mode 100644
index 0000000000..3140034920
--- /dev/null
+++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.html
@@ -0,0 +1,936 @@
+
+
+Reference
+
+
+
+

Reference

+
+
+

Global namespace

+
+

Namespaces

+ + + + + + + + + + + +
NameDescription
regular_ns

A regular namespace with different filters for members

+ + +
see_below_ns

A see-below namespace

+ + +
+

Namespace Aliases

+ + + + + + + + + + + + +
NameDescription
dependency_ns_alias

Namespace alias to form the dependency on dependency_ns

+ + +
implementation_defined_ns_alias

Namespace alias to form a dependency on the implementation-defined namespace

+ + +
see_below_ns_alias

Namespace alias to form a dependency on the see-below namespace

+ + +
+

Types

+ + + + + + + + + + + +
NameDescription
regular

A regular symbol in the global namespace

+ + +
see_below

A see-below symbol in the global namespace

+ + +
+

Functions

+ + + + + + + + + + + + + +
NameDescription
get_dependency

A function to get a dependency symbol on the global namespace

+ + +
get_implementation_defined

A function to get an implementation-defined symbol in the global namespace

+ + +
get_regular

A function to get a regular symbol in the global namespace

+ + +
get_see_below

A function to get a see-below symbol in the global namespace

+ + +
+
+
+
+

regular_ns

+
+

A regular namespace with different filters for members

+ + + +
+
+

Types

+ + + + + + + + + + + +
NameDescription
regular

A symbol that passes the filters

+ + +
see_below

A symbol that passes the see-below filter

+ + +
+

Functions

+ + + + + + + + + + + + + +
NameDescription
get_dependency

A function to get an excluded symbol

+ + +
get_implementation_defined

A function to get an implementation-defined symbol

+ + +
get_regular

A function to get a regular symbol

+ + +
get_see_below

A function to get a see-below symbol

+ + +
+
+
+
+

regular_ns::regular

+
+

A symbol that passes the filters

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct regular;
+
+
+
+

Types

+ + + + + + + + + + +
NameDescription
also_regular

Child of a regular symbol extracted as regular

+ + +
+ + +
+

Description

+

The symbol should have a page as usual

+ + + +
+
+
+
+

regular_ns::regular::also_regular

+
+

Child of a regular symbol extracted as regular

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct also_regular;
+
+
+
+

Types

+ + + + + + + + + + +
NameDescription
regular_as_well

Grandchild of a regular symbol extracted as regular

+ + +
+ + +
+
+
+

regular_ns::regular::also_regular::regular_as_well

+
+

Grandchild of a regular symbol extracted as regular

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct regular_as_well;
+
+
+
+ + +
+
+
+

regular_ns::see_below

+
+

A symbol that passes the see-below filter

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct see_below { /* see-below */ };
+
+
+
+ + +
+

Description

+

A symbol that passes the filters and the see-below filter. The symbol should have a page as usual but, because it's a scope +and not a namespace, the members should not be listed on that page.

+ + + +
+
+
+
+

regular_ns::get_dependency

+
+

A function to get an excluded symbol

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+dependency
+get_dependency();
+
+
+
+
+

Description

+

When used in a function, only the symbol name should be shown. No links should be generated for this symbol.

+ + + +
+
+
+
+

regular_ns::get_implementation_defined

+
+

A function to get an implementation-defined symbol

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+/* implementation-defined */
+get_implementation_defined();
+
+
+
+
+

Description

+

When used in a function, the implementation-defined comment should replace the real type.

+ +

It's the responsibility of the function documentation to explain the implementation-defined symbol.

+ + + +
+
+
+
+

regular_ns::get_regular

+
+

A function to get a regular symbol

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+regular
+get_regular();
+
+
+
+
+

Description

+

When used in a function, the symbol should be shown as usual with a link to the page.

+ + + +
+
+
+
+

regular_ns::get_see_below

+
+

A function to get a see-below symbol

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+see_below
+get_see_below();
+
+
+
+
+

Description

+

When used in a function, the symbol name should be shown as usual. +The page for this symbol is what should be different because +the synopsis should say "See below" and the members are not +listed unless it's a namespace or the symbol has been explicitly +used as a dependency elsewhere.

+ + + +
+
+
+
+

see_below_ns

+
+

A see-below namespace

+ + + +
+
+

Types

+ + + + + + + + + + + +
NameDescription
regular

Regular symbol in a see-below namespace

+ + +
see_below

See-below symbol in a see-below namespace

+ + +
+

Functions

+ + + + + + + + + + + +
NameDescription
get_dependency

A function to get a dependency symbol in a see-below namespace

+ + +
get_implementation_defined

A function to get an implementation-defined symbol in a see-below namespace

+ + +
+
+

Description

+

All member symbols should become see-below. All members are traversed as see-below.

+ +

The documentation page for these symbols should include the see-below comment.

+ + + +
+
+
+
+

see_below_ns::regular

+
+

Regular symbol in a see-below namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct regular { /* see-below */ };
+
+
+
+ + +
+

Description

+

The symbol becomes see-below because the whole namespace is see-below.

+ + + +
+
+
+
+

see_below_ns::see_below

+
+

See-below symbol in a see-below namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct see_below { /* see-below */ };
+
+
+
+ + +
+

Description

+

The symbol becomes see-below because the whole namespace is see-below and because it's explicitly marked as see-below.

+ + + +
+
+
+
+

see_below_ns::get_dependency

+
+

A function to get a dependency symbol in a see-below namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+dependency
+get_dependency();
+
+
+
+
+

Description

+

The symbol should be extracted as a dependency because the exclude filter has precedence over the see-below filter. +Only included symbols can be promoted to see-below.

+ +

It's the responsibility of the function documentation to explain the dependency.

+ + + +
+
+
+
+

see_below_ns::get_implementation_defined

+
+

A function to get an implementation-defined symbol in a see-below namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+/* implementation-defined */
+get_implementation_defined();
+
+
+
+
+

Description

+

When used in a function, the implementation-defined comment should replace the real type.

+ +

It's the responsibility of the function documentation to explain the implementation-defined symbol.

+ + + +
+
+
+
+

regular

+
+

A regular symbol in the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct regular;
+
+
+
+

Types

+ + + + + + + + + + +
NameDescription
also_regular

Child of a regular symbol: should be traversed as usual

+ + +
+ + +
+

Description

+

This symbol should have a page as usual.

+ + + +
+
+
+
+

regular::also_regular

+
+

Child of a regular symbol: should be traversed as usual

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct also_regular;
+
+
+
+

Types

+ + + + + + + + + + +
NameDescription
regular_as_well

Grandchild of a regular symbol: should be traversed as usual

+ + +
+ + +
+
+
+

regular::also_regular::regular_as_well

+
+

Grandchild of a regular symbol: should be traversed as usual

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct regular_as_well;
+
+
+
+ + +
+
+
+

see_below

+
+

A see-below symbol in the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+struct see_below { /* see-below */ };
+
+
+
+ + +
+

Description

+

This symbol should have a page as usual but, because it's a scope +and not a namespace, the members should not be listed on that page.

+ +

The synopsis should say "See below".

+ + + +
+
+
+
+

get_dependency

+
+

A function to get a dependency symbol on the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+dependency
+get_dependency();
+
+
+
+
+

Description

+

The symbol should be extracted as a dependency but its members should not be traversed.

+ + + +
+
+
+
+

get_implementation_defined

+
+

A function to get an implementation-defined symbol in the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+/* implementation-defined */
+get_implementation_defined();
+
+
+
+
+

Description

+

When used in a function, the implementation-defined comment should replace the real type.

+ +

It's the responsibility of the function documentation to explain the implementation-defined symbol.

+ + + +
+
+
+
+

get_regular

+
+

A function to get a regular symbol in the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+regular
+get_regular();
+
+
+
+
+

Description

+

When used in a function, the symbol should be shown as usual with a link to the page.

+ + + +
+
+
+
+

get_see_below

+
+

A function to get a see-below symbol in the global namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+see_below
+get_see_below();
+
+
+
+
+

Description

+

When used in a function, the symbol name should be shown as usual. +The page for this symbol is what should be different because +the synopsis should say "See below" and the members are not +listed unless it's a namespace or the symbol has been explicitly +used as a dependency elsewhere.

+ + + +
+
+
+
+

dependency_ns_alias

+
+

Namespace alias to form the dependency on dependency_ns

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+namespace dependency_ns_alias = dependency_ns;
+
+
+
+
+
+
+

implementation_defined_ns_alias

+
+

Namespace alias to form a dependency on the implementation-defined namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+namespace implementation_defined_ns_alias = /* implementation-defined */;
+
+
+
+
+
+
+

see_below_ns_alias

+
+

Namespace alias to form a dependency on the see-below namespace

+ + + +
+
+
+

Synopsis

+
+Declared in <extraction-mode.cpp>
+
+
+namespace see_below_ns_alias = see_below_ns;
+
+
+
+
+

Description

+

The alias should be linked as usual and, because it's a namespace, +the members should be listed on the page.

+ + + +
+
+ +
+
+

Created with MrDocs

+
+ + \ No newline at end of file diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.xml b/test-files/golden-tests/filters/symbol-name/extraction-mode.xml new file mode 100644 index 0000000000..3076c6d5ba --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.xml @@ -0,0 +1,432 @@ + + + + + + + A regular namespace with different filters for members + + + + + + + A symbol that passes the filters + + + The symbol should have a page as usual + + + + + + + Child of a regular symbol extracted as regular + + + + + + + Grandchild of a regular symbol extracted as regular + + + + + + + + + + + + + A function to get a regular symbol + + + When used in a function, the symbol should be shown as usual + with a link to the page. + + + + + + + + A symbol that passes the see-below filter + + + A symbol that passes the filters and the see-below filter. + The symbol should have a page as usual but, because it's a scope + and not a namespace, the members should not be listed on that page. + + + + + + + + + + + A function to get a see-below symbol + + + When used in a function, the symbol name should be shown as usual. + The page for this symbol is what should be different because + the synopsis should say "See below" and the members are not + listed unless it's a namespace or the symbol has been explicitly + used as a dependency elsewhere. + + + + + + + + A symbol that passes the implementation-defined filter + + + A symbol that passes the filters and the implementation-defined filter + The symbol is implementation defined and should not have a page. + Members of an implementation-defined scope should not be traversed. + If they are traversed for some other reason, they should also become + implementation-defined. + + + + + + + + + + + A function to get an implementation-defined symbol + + + When used in a function, the implementation-defined + comment should replace the real type. + + + It's the responsibility of the function documentation + to explain the implementation-defined symbol. + + + + + + + + An excluded symbol used as a dependency by a regular symbol + + + A symbol excluded by filters but is used as a dependency + The symbol should be extracted as a dependency but its + members should not be traversed. + + + + + + + + + + + A function to get an excluded symbol + + + When used in a function, only the symbol name should be shown. + No links should be generated for this symbol. + + + + + + + + A see-below namespace + + + All member symbols should become see-below. All members are + traversed as see-below. + + + The documentation page for these symbols should include + the see-below comment. + + + + + + + Regular symbol in a see-below namespace + + + The symbol becomes see-below because the whole namespace + is see-below. + + + + + + + + See-below symbol in a see-below namespace + + + The symbol becomes see-below because the whole namespace + is see-below and because it's explicitly marked as see-below. + + + + + + + + Implementation-defined symbol in a see-below namespace + + + The symbol does not become see-below because the + the implentation-defined filter has precedence over the + see-below filter. + + + Functions using this symbol should explain the implementation-defined + nature of the symbol. + + + + + + + + + + + A function to get an implementation-defined symbol in a see-below namespace + + + When used in a function, the implementation-defined + comment should replace the real type. + + + It's the responsibility of the function documentation + to explain the implementation-defined symbol. + + + + + + + + A dependency symbol in a see-below namespace + + + The symbol should be extracted as a dependency because the + exclude filter has precedence over the see-below filter. + Only included symbols can be promoted to see-below. + + + This will not have a page and functions using this symbol + should explain the dependency. + + + + + + + + + + + A function to get a dependency symbol in a see-below namespace + + + The symbol should be extracted as a dependency because the + exclude filter has precedence over the see-below filter. + Only included symbols can be promoted to see-below. + + + It's the responsibility of the function documentation + to explain the dependency. + + + + + + + + + Namespace alias to form a dependency on the see-below namespace + + + The alias should be linked as usual and, because it's a namespace, + the members should be listed on the page. + + + + + + + + An implementation-defined namespace + + + Members are not traversed and, if traversed for some + other reason, they also become implementation-defined. + + + + + + + + Namespace alias to form a dependency on the implementation-defined namespace + + + + + + + + + + + Namespace alias to form the dependency on dependency_ns + + + + + + + + + A regular symbol in the global namespace + + + This symbol should have a page as usual. + + + + + + + Child of a regular symbol: should be traversed as usual + + + + + + + Grandchild of a regular symbol: should be traversed as usual + + + + + + + + + + + + + A function to get a regular symbol in the global namespace + + + When used in a function, the symbol should be shown as usual + with a link to the page. + + + + + + + + A see-below symbol in the global namespace + + + This symbol should have a page as usual but, because it's a scope + and not a namespace, the members should not be listed on that page. + + + The synopsis should say "See below". + + + + + + + + + + + A function to get a see-below symbol in the global namespace + + + When used in a function, the symbol name should be shown as usual. + The page for this symbol is what should be different because + the synopsis should say "See below" and the members are not + listed unless it's a namespace or the symbol has been explicitly + used as a dependency elsewhere. + + + + + + + + An implementation-defined symbol in the global namespace + + + The symbol is implementation defined and should not have a page. + + + + + + + + + + + A function to get an implementation-defined symbol in the global namespace + + + When used in a function, the implementation-defined + comment should replace the real type. + + + It's the responsibility of the function documentation + to explain the implementation-defined symbol. + + + + + + + + A dependency symbol in the global namespace + + + + + + + + + + + A function to get a dependency symbol on the global namespace + + + The symbol should be extracted as a dependency but its + members should not be traversed. + + + + + diff --git a/test-files/golden-tests/filters/symbol-name/extraction-mode.yml b/test-files/golden-tests/filters/symbol-name/extraction-mode.yml new file mode 100644 index 0000000000..6bd1b3b05f --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/extraction-mode.yml @@ -0,0 +1,31 @@ +exclude-symbols: + - 'regular_ns::dependency' + - 'regular_ns::excluded' + - 'see_below_ns::dependency' + - 'see_below_ns::excluded' + - 'implementation_defined_ns::dependency' + - 'implementation_defined_ns::excluded' + - 'dependency_ns::dependency' + - 'dependency_ns::excluded' + - 'excluded_ns::dependency' + - 'excluded_ns::excluded' + - 'dependency_ns' + - 'excluded_ns' + - 'dependency' + - 'excluded' +see-below: + - 'regular_ns::see_below' + - 'see_below_ns::see_below' + - 'implementation_defined_ns::see_below' + - 'dependency_ns::see_below' + - 'excluded_ns::see_below' + - 'see_below_ns' + - 'see_below' +implementation-defined: + - 'regular_ns::implementation_defined' + - 'see_below_ns::implementation_defined' + - 'implementation_defined_ns::implementation_defined' + - 'dependency_ns::implementation_defined' + - 'excluded_ns::implementation_defined' + - 'implementation_defined_ns' + - 'implementation_defined' diff --git a/test-files/golden-tests/filters/symbol-name/impl-defined-member.adoc b/test-files/golden-tests/filters/symbol-name/impl-defined-member.adoc new file mode 100644 index 0000000000..e597e539f7 --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/impl-defined-member.adoc @@ -0,0 +1,85 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + +=== Namespaces +[cols=2] +|=== +| Name | Description + +| <<#regular,`regular`>> +| + +|=== +=== Variables +[cols=2] +|=== +| Name | Description + +| <<#absolute_uri_rule,`absolute_uri_rule`>> +| + +| <<#regular_absolute_uri_rule,`regular_absolute_uri_rule`>> +| + +|=== + +[#regular] +== regular + +=== Types +[cols=2] +|=== +| Name | Description + +| <<#regular-absolute_uri_rule_t,`absolute_uri_rule_t`>> +| + +|=== + +[#regular-absolute_uri_rule_t] +== <<#regular,regular>>::absolute_uri_rule_t + +=== Synopsis + +Declared in `<impl‐defined‐member.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +struct absolute_uri_rule_t; +---- + + + + +[#absolute_uri_rule] +== absolute_uri_rule + +=== Synopsis + +Declared in `<impl‐defined‐member.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +constexpr +/* implementation-defined */ const absolute_uri_rule = {}; +---- + +[#regular_absolute_uri_rule] +== regular_absolute_uri_rule + +=== Synopsis + +Declared in `<impl‐defined‐member.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +constexpr +<<#regular,regular>>::<<#regular-absolute_uri_rule_t,absolute_uri_rule_t>> const regular_absolute_uri_rule = {}; +---- + + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/filters/symbol-name/impl-defined-member.cpp b/test-files/golden-tests/filters/symbol-name/impl-defined-member.cpp new file mode 100644 index 0000000000..6e5c7cd903 --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/impl-defined-member.cpp @@ -0,0 +1,11 @@ +namespace detail { + struct absolute_uri_rule_t {}; +} + +namespace regular { + struct absolute_uri_rule_t {}; +} + +constexpr detail::absolute_uri_rule_t absolute_uri_rule = {}; + +constexpr regular::absolute_uri_rule_t regular_absolute_uri_rule = {}; diff --git a/test-files/golden-tests/temp/ct_mct_expl_outside.html b/test-files/golden-tests/filters/symbol-name/impl-defined-member.html similarity index 50% rename from test-files/golden-tests/temp/ct_mct_expl_outside.html rename to test-files/golden-tests/filters/symbol-name/impl-defined-member.html index 54129069cb..e53d79460f 100644 --- a/test-files/golden-tests/temp/ct_mct_expl_outside.html +++ b/test-files/golden-tests/filters/symbol-name/impl-defined-member.html @@ -9,7 +9,7 @@

Reference

Global namespace

-

Types

+

Namespaces

@@ -18,25 +18,29 @@

Types

- + +
A +regular +
+

Variables

+ + + + + + + + + +
NameDescription
absolute_uri_rule +
regular_absolute_uri_rule
-

A

-
-
-

Synopsis

-
-Declared in <temp/ct_mct_expl_outside.cpp>
-
-
-template<typename T>
-struct A;
-
-
+

regular

Types

@@ -47,56 +51,56 @@

Types

-
B +absolute_uri_rule_t
- -
-

A::B

+

regular::absolute_uri_rule_t

Synopsis

-Declared in <temp/ct_mct_expl_outside.cpp>
+Declared in <impl-defined-member.cpp>
 
-template<typename U>
-struct B;
+struct absolute_uri_rule_t;
 
 
-

Member Functions

- - - - - - - - - - -
NameDescription
f -
-

A::B::f

+

absolute_uri_rule

+
+
+

Synopsis

+
+Declared in <impl-defined-member.cpp>
+
+
+constexpr
+/* implementation-defined */ const absolute_uri_rule = {};
+
+
+
+
+
+
+

regular_absolute_uri_rule

Synopsis

-Declared in <temp/ct_mct_expl_outside.cpp>
+Declared in <impl-defined-member.cpp>
 
-void
-f();
+constexpr
+regular::absolute_uri_rule_t const regular_absolute_uri_rule = {};
 
 
diff --git a/test-files/golden-tests/filters/symbol-name/impl-defined-member.xml b/test-files/golden-tests/filters/symbol-name/impl-defined-member.xml new file mode 100644 index 0000000000..692a10746a --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/impl-defined-member.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test-files/golden-tests/filters/symbol-name/impl-defined-member.yml b/test-files/golden-tests/filters/symbol-name/impl-defined-member.yml new file mode 100644 index 0000000000..f0855b2265 --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/impl-defined-member.yml @@ -0,0 +1,2 @@ +implementation-defined: + - detail \ No newline at end of file diff --git a/test-files/golden-tests/filters/symbol-name/mrdocs.yml b/test-files/golden-tests/filters/symbol-name/mrdocs.yml new file mode 100644 index 0000000000..63cf197efb --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/mrdocs.yml @@ -0,0 +1 @@ +source-root: . \ No newline at end of file diff --git a/test-files/golden-tests/filters/whitelist_0.adoc b/test-files/golden-tests/filters/symbol-name/whitelist_0.adoc similarity index 52% rename from test-files/golden-tests/filters/whitelist_0.adoc rename to test-files/golden-tests/filters/symbol-name/whitelist_0.adoc index afa6df0dbc..cf5cc1905b 100644 --- a/test-files/golden-tests/filters/whitelist_0.adoc +++ b/test-files/golden-tests/filters/symbol-name/whitelist_0.adoc @@ -38,7 +38,7 @@ === Synopsis -Declared in `<filters/whitelist_0.cpp>` +Declared in `<whitelist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- @@ -65,28 +65,6 @@ f0_WL(); [#N1-N3_WL] == <<#N1,N1>>::N3_WL -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#N1-N3_WL-f1_WL,`f1_WL`>> -| - -|=== - -[#N1-N3_WL-f1_WL] -== <<#N1,N1>>::<<#N1-N3_WL,N3_WL>>::f1_WL - -=== Synopsis - -Declared in `<filters/whitelist_0.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f1_WL(); ----- [#N1-N4] == <<#N1,N1>>::N4 @@ -106,7 +84,7 @@ f1_WL(); === Synopsis -Declared in `<filters/whitelist_0.cpp>` +Declared in `<whitelist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- @@ -126,28 +104,6 @@ f1_WL(); | |=== -=== Functions -[cols=2] -|=== -| Name | Description - -| <<#N5-f0,`f0`>> -| - -|=== - -[#N5-f0] -== <<#N5,N5>>::f0 - -=== Synopsis - -Declared in `<filters/whitelist_0.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f0(); ----- [#N5-N6_BL] == <<#N5,N5>>::N6_BL @@ -163,58 +119,43 @@ f0(); | <<#N5-N6_BL-N7,`N7`>> | -|=== - -[#N5-N6_BL-N7] -== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::N7 +| <<#N5-N6_BL-N8,`N8`>> +| +|=== === Functions [cols=2] |=== | Name | Description -| <<#N5-N6_BL-N7-f2_WL,`f2_WL`>> +| <<#N5-N6_BL-f1_BL,`f1_BL`>> | |=== -[#N5-N6_BL-N7-f2_WL] -== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::<<#N5-N6_BL-N7,N7>>::f2_WL - -=== Synopsis - -Declared in `<filters/whitelist_0.cpp>` - -[source,cpp,subs="verbatim,replacements,macros,-callouts"] ----- -void -f2_WL(); ----- - [#N5-N6_BL-M7] == <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::M7 -=== Functions -[cols=2] -|=== -| Name | Description -| <<#N5-N6_BL-M7-f2_WL,`f2_WL`>> -| +[#N5-N6_BL-N7] +== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::N7 + + +[#N5-N6_BL-N8] +== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::N8 -|=== -[#N5-N6_BL-M7-f2_WL] -== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::<<#N5-N6_BL-M7,M7>>::f2_WL +[#N5-N6_BL-f1_BL] +== <<#N5,N5>>::<<#N5-N6_BL,N6_BL>>::f1_BL === Synopsis -Declared in `<filters/whitelist_0.cpp>` +Declared in `<whitelist_0.cpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- void -f2_WL(); +f1_BL(); ---- diff --git a/test-files/golden-tests/filters/whitelist_0.cpp b/test-files/golden-tests/filters/symbol-name/whitelist_0.cpp similarity index 100% rename from test-files/golden-tests/filters/whitelist_0.cpp rename to test-files/golden-tests/filters/symbol-name/whitelist_0.cpp diff --git a/test-files/golden-tests/filters/whitelist_0.html b/test-files/golden-tests/filters/symbol-name/whitelist_0.html similarity index 62% rename from test-files/golden-tests/filters/whitelist_0.html rename to test-files/golden-tests/filters/symbol-name/whitelist_0.html index 5522db714b..747c075505 100644 --- a/test-files/golden-tests/filters/whitelist_0.html +++ b/test-files/golden-tests/filters/symbol-name/whitelist_0.html @@ -52,7 +52,7 @@

N0::f0_WL

Synopsis

-Declared in <filters/whitelist_0.cpp>
+Declared in <whitelist_0.cpp>
 
 void
@@ -85,35 +85,6 @@ 

Namespaces

N1::N3_WL

-

Functions

- - - - - - - - - - -
NameDescription
f1_WL -
- -
-
-

N1::N3_WL::f1_WL

-
-
-

Synopsis

-
-Declared in <filters/whitelist_0.cpp>
-
-
-void
-f1_WL();
-
-
-
@@ -140,7 +111,7 @@

N1::N4::f1_WL

Synopsis

-Declared in <filters/whitelist_0.cpp>
+Declared in <whitelist_0.cpp>
 
 void
@@ -166,35 +137,6 @@ 

Namespaces

-

Functions

- - - - - - - - - - -
NameDescription
f0 -
-
-
-
-

N5::f0

-
-
-

Synopsis

-
-Declared in <filters/whitelist_0.cpp>
-
-
-void
-f0();
-
-
-
@@ -212,14 +154,11 @@

Namespaces

M7 N7 + +N8 -
-
-
-

N5::N6_BL::N7

-

Functions

@@ -229,57 +168,38 @@

Functions

-
f2_WL +f1_BL
-

N5::N6_BL::N7::f2_WL

+

N5::N6_BL::M7

+
-

Synopsis

-Declared in <filters/whitelist_0.cpp>
-
-
-void
-f2_WL();
-
-
+

N5::N6_BL::N7

-

N5::N6_BL::M7

+

N5::N6_BL::N8

-

Functions

- - - - - - - - - - -
NameDescription
f2_WL -
-

N5::N6_BL::M7::f2_WL

+

N5::N6_BL::f1_BL

Synopsis

-Declared in <filters/whitelist_0.cpp>
+Declared in <whitelist_0.cpp>
 
 void
-f2_WL();
+f1_BL();
 
 
diff --git a/test-files/golden-tests/filters/whitelist_0.xml b/test-files/golden-tests/filters/symbol-name/whitelist_0.xml similarity index 62% rename from test-files/golden-tests/filters/whitelist_0.xml rename to test-files/golden-tests/filters/symbol-name/whitelist_0.xml index 013f393d36..b6214eb515 100644 --- a/test-files/golden-tests/filters/whitelist_0.xml +++ b/test-files/golden-tests/filters/symbol-name/whitelist_0.xml @@ -4,35 +4,28 @@ - + - - - - + - - - + + + - - - - - - + + diff --git a/test-files/golden-tests/filters/symbol-name/whitelist_0.yml b/test-files/golden-tests/filters/symbol-name/whitelist_0.yml new file mode 100644 index 0000000000..8e00d807ae --- /dev/null +++ b/test-files/golden-tests/filters/symbol-name/whitelist_0.yml @@ -0,0 +1,5 @@ +include-symbols: + - 'N0::f0_WL' + - 'N1::N3_WL' + - 'N1::N4::f1_WL' + - 'N5::N6_BL::*7' diff --git a/test-files/golden-tests/filters/symbol-type/mrdocs.yml b/test-files/golden-tests/filters/symbol-type/mrdocs.yml new file mode 100644 index 0000000000..63cf197efb --- /dev/null +++ b/test-files/golden-tests/filters/symbol-type/mrdocs.yml @@ -0,0 +1 @@ +source-root: . \ No newline at end of file diff --git a/test-files/golden-tests/nested-private-template.adoc b/test-files/golden-tests/filters/symbol-type/nested-private-template.adoc similarity index 100% rename from test-files/golden-tests/nested-private-template.adoc rename to test-files/golden-tests/filters/symbol-type/nested-private-template.adoc diff --git a/test-files/golden-tests/nested-private-template.cpp b/test-files/golden-tests/filters/symbol-type/nested-private-template.cpp similarity index 100% rename from test-files/golden-tests/nested-private-template.cpp rename to test-files/golden-tests/filters/symbol-type/nested-private-template.cpp diff --git a/test-files/golden-tests/nested-private-template.html b/test-files/golden-tests/filters/symbol-type/nested-private-template.html similarity index 100% rename from test-files/golden-tests/nested-private-template.html rename to test-files/golden-tests/filters/symbol-type/nested-private-template.html diff --git a/test-files/golden-tests/nested-private-template.xml b/test-files/golden-tests/filters/symbol-type/nested-private-template.xml similarity index 88% rename from test-files/golden-tests/nested-private-template.xml rename to test-files/golden-tests/filters/symbol-type/nested-private-template.xml index d4d4593f69..aef1bd90f7 100644 --- a/test-files/golden-tests/nested-private-template.xml +++ b/test-files/golden-tests/filters/symbol-type/nested-private-template.xml @@ -5,12 +5,12 @@