From a42ad72dfc2b1b767453af96b6438f997e3a8d5f Mon Sep 17 00:00:00 2001 From: Gennaro Prota Date: Thu, 30 Apr 2026 15:38:01 +0200 Subject: [PATCH] feat: support macros This adds support for preprocessing macros: `#define` directives are captured via a `clang::PPCallbacks` subclass and exposed as `MacroSymbol` instances at the corpus root (macros aren't in any C++ scope, so they sit alongside the global namespace rather than under it). Doc-comment association: Clang doesn't link comments to macros, so we look up a preceding doc-comment by source location. A comment is attached only when the lines between it and the directive are blank. Filters apply to macros, too: - `extract-all`: when off, undocumented macros are dropped. - `exclude-symbols`, `include-symbols`, `implementation-defined`, `see-below`: matched against the macro name. - File-pattern filters: matched against the directive's source location. Schema additions: `` is a new top-level element in mrdocs.rnc, sibling of ``, with `` carrying the verbatim definition (modulo a minor normalization). New output pages: multipage mode now produces `macros.{ext}` at the output root, listing every macro with a link to its per-symbol page. The global-namespace page gains a "See also: Macros" navigation hint at the bottom (multipage-only) so the new page is discoverable. Macro `@param` blocks are validated. Golden tests also cover the three filter paths (`extract-all: false`, `include-symbols`, `exclude-symbols`). Closes issue #1127. --- include/mrdocs/Corpus.hpp | 13 + include/mrdocs/Metadata.hpp | 1 + include/mrdocs/Metadata/Symbol/Macro.hpp | 82 +++ include/mrdocs/Metadata/Symbol/SymbolKind.hpp | 2 +- .../mrdocs/Metadata/Symbol/SymbolNodes.inc | 1 + mrdocs.rnc | 13 + .../adoc/layouts/macros-index.adoc.hbs | 6 + .../generator/adoc/partials/symbol.adoc.hbs | 6 + .../partials/symbol/signature/macro.hbs | 18 + .../html/layouts/macros-index.html.hbs | 6 + .../generator/html/partials/symbol.html.hbs | 5 + src/lib/AST/ASTAction.cpp | 13 +- src/lib/AST/ASTAction.hpp | 4 + src/lib/AST/ASTVisitor.cpp | 190 ++++- src/lib/AST/ASTVisitor.hpp | 38 +- src/lib/AST/ASTVisitorConsumer.cpp | 3 +- src/lib/AST/ASTVisitorConsumer.hpp | 7 +- src/lib/AST/ExtractDocComment.cpp | 10 +- src/lib/AST/ExtractDocComment.hpp | 13 +- src/lib/AST/MacroCollector.cpp | 197 ++++++ src/lib/AST/MacroCollector.hpp | 91 +++ src/lib/Corpus.cpp | 42 ++ src/lib/Gen/adoc/AdocGenerator.cpp | 8 + src/lib/Gen/adoc/AdocGenerator.hpp | 5 + src/lib/Gen/hbs/Builder.cpp | 83 ++- src/lib/Gen/hbs/Builder.hpp | 12 + src/lib/Gen/hbs/HandlebarsGenerator.cpp | 70 ++ src/lib/Gen/hbs/HandlebarsGenerator.hpp | 9 + src/lib/Gen/hbs/SinglePageVisitor.cpp | 14 + src/lib/Gen/hbs/SinglePageVisitor.hpp | 9 + src/lib/Gen/html/HTMLGenerator.cpp | 12 + src/lib/Gen/html/HTMLGenerator.hpp | 5 + src/lib/Gen/xml/XMLWriter.cpp | 7 + .../Finalizers/DocComment/Function.hpp | 8 +- .../Finalizers/DocCommentFinalizer.cpp | 91 ++- .../Finalizers/DocCommentFinalizer.hpp | 6 + .../exclude-symbols/macros-excluded.adoc | 40 ++ .../exclude-symbols/macros-excluded.cpp | 13 + .../exclude-symbols/macros-excluded.html | 57 ++ .../exclude-symbols/macros-excluded.xml | 65 ++ .../exclude-symbols/macros-excluded.yml | 2 + .../extract-all/no-extract-all-macros.adoc | 50 ++ .../extract-all/no-extract-all-macros.cpp | 14 + .../extract-all/no-extract-all-macros.html | 75 ++ .../extract-all/no-extract-all-macros.xml | 73 ++ .../extract-all/no-extract-all-macros.yml | 2 + .../include-symbols/macros-allowlist.adoc | 54 ++ .../include-symbols/macros-allowlist.cpp | 13 + .../include-symbols/macros-allowlist.html | 73 ++ .../include-symbols/macros-allowlist.xml | 90 +++ .../include-symbols/macros-allowlist.yml | 2 + .../config/multipage/multipage-macros.cpp | 23 + .../adoc/MY_INC.adoc | 27 + .../adoc/MY_VERSION.adoc | 17 + .../multipage-macros.multipage/adoc/foo.adoc | 15 + .../adoc/foo/bar.adoc | 19 + .../adoc/index.adoc | 27 + .../adoc/macros.adoc | 14 + .../html/MY_INC.html | 43 ++ .../html/MY_VERSION.html | 25 + .../multipage-macros.multipage/html/foo.html | 29 + .../html/foo/bar.html | 26 + .../html/index.html | 46 ++ .../html/macros.html | 27 + .../xml/reference.xml | 124 ++++ .../config/multipage/multipage-macros.yml | 1 + .../symbols/macro/macros-edge-cases.adoc | 74 ++ .../symbols/macro/macros-edge-cases.cpp | 28 + .../symbols/macro/macros-edge-cases.html | 93 +++ .../symbols/macro/macros-edge-cases.xml | 109 +++ .../symbols/macro/macros-embedded.adoc | 45 ++ .../symbols/macro/macros-embedded.cpp | 14 + .../symbols/macro/macros-embedded.html | 60 ++ .../symbols/macro/macros-embedded.xml | 73 ++ .../symbols/macro/macros-embedded.yml | 1 + .../macro/macros-extraction-modes.adoc | 60 ++ .../symbols/macro/macros-extraction-modes.cpp | 34 + .../macro/macros-extraction-modes.html | 86 +++ .../symbols/macro/macros-extraction-modes.xml | 193 ++++++ .../symbols/macro/macros-extraction-modes.yml | 4 + .../symbols/macro/macros-multi-file.adoc | 36 + .../symbols/macro/macros-multi-file.cpp | 10 + .../symbols/macro/macros-multi-file.hpp | 5 + .../symbols/macro/macros-multi-file.html | 51 ++ .../symbols/macro/macros-multi-file.xml | 43 ++ .../golden-tests/symbols/macro/macros.adoc | 397 +++++++++++ .../golden-tests/symbols/macro/macros.cpp | 118 ++++ .../golden-tests/symbols/macro/macros.html | 531 ++++++++++++++ .../golden-tests/symbols/macro/macros.xml | 649 ++++++++++++++++++ 89 files changed, 4875 insertions(+), 25 deletions(-) create mode 100644 include/mrdocs/Metadata/Symbol/Macro.hpp create mode 100644 share/mrdocs/addons/generator/adoc/layouts/macros-index.adoc.hbs create mode 100644 share/mrdocs/addons/generator/common/partials/symbol/signature/macro.hbs create mode 100644 share/mrdocs/addons/generator/html/layouts/macros-index.html.hbs create mode 100644 src/lib/AST/MacroCollector.cpp create mode 100644 src/lib/AST/MacroCollector.hpp create mode 100644 test-files/golden-tests/config/exclude-symbols/macros-excluded.adoc create mode 100644 test-files/golden-tests/config/exclude-symbols/macros-excluded.cpp create mode 100644 test-files/golden-tests/config/exclude-symbols/macros-excluded.html create mode 100644 test-files/golden-tests/config/exclude-symbols/macros-excluded.xml create mode 100644 test-files/golden-tests/config/exclude-symbols/macros-excluded.yml create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all-macros.adoc create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all-macros.cpp create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all-macros.html create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all-macros.xml create mode 100644 test-files/golden-tests/config/extract-all/no-extract-all-macros.yml create mode 100644 test-files/golden-tests/config/include-symbols/macros-allowlist.adoc create mode 100644 test-files/golden-tests/config/include-symbols/macros-allowlist.cpp create mode 100644 test-files/golden-tests/config/include-symbols/macros-allowlist.html create mode 100644 test-files/golden-tests/config/include-symbols/macros-allowlist.xml create mode 100644 test-files/golden-tests/config/include-symbols/macros-allowlist.yml create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.cpp create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_INC.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_VERSION.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo/bar.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/index.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/macros.adoc create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_INC.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_VERSION.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo/bar.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/index.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/html/macros.html create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.multipage/xml/reference.xml create mode 100644 test-files/golden-tests/config/multipage/multipage-macros.yml create mode 100644 test-files/golden-tests/symbols/macro/macros-edge-cases.adoc create mode 100644 test-files/golden-tests/symbols/macro/macros-edge-cases.cpp create mode 100644 test-files/golden-tests/symbols/macro/macros-edge-cases.html create mode 100644 test-files/golden-tests/symbols/macro/macros-edge-cases.xml create mode 100644 test-files/golden-tests/symbols/macro/macros-embedded.adoc create mode 100644 test-files/golden-tests/symbols/macro/macros-embedded.cpp create mode 100644 test-files/golden-tests/symbols/macro/macros-embedded.html create mode 100644 test-files/golden-tests/symbols/macro/macros-embedded.xml create mode 100644 test-files/golden-tests/symbols/macro/macros-embedded.yml create mode 100644 test-files/golden-tests/symbols/macro/macros-extraction-modes.adoc create mode 100644 test-files/golden-tests/symbols/macro/macros-extraction-modes.cpp create mode 100644 test-files/golden-tests/symbols/macro/macros-extraction-modes.html create mode 100644 test-files/golden-tests/symbols/macro/macros-extraction-modes.xml create mode 100644 test-files/golden-tests/symbols/macro/macros-extraction-modes.yml create mode 100644 test-files/golden-tests/symbols/macro/macros-multi-file.adoc create mode 100644 test-files/golden-tests/symbols/macro/macros-multi-file.cpp create mode 100644 test-files/golden-tests/symbols/macro/macros-multi-file.hpp create mode 100644 test-files/golden-tests/symbols/macro/macros-multi-file.html create mode 100644 test-files/golden-tests/symbols/macro/macros-multi-file.xml create mode 100644 test-files/golden-tests/symbols/macro/macros.adoc create mode 100644 test-files/golden-tests/symbols/macro/macros.cpp create mode 100644 test-files/golden-tests/symbols/macro/macros.html create mode 100644 test-files/golden-tests/symbols/macro/macros.xml diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index 1d09ef486b..7e7df74a1e 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -155,6 +155,19 @@ class MRDOCS_VISIBLE NamespaceSymbol const& globalNamespace() const noexcept; + /** Return all preprocessor macros in the corpus. + + Macros are stored at the corpus root, not under any + namespace, since they are not in any C++ scope. The + result is sorted by macro name, with source location + as a tiebreaker, so iteration order is stable across + runs. + + @return All MacroSymbols in stable iteration order. + */ + std::vector + macros() const; + /** Visit the specified Symbol IDs This function invokes the specified function `f` diff --git a/include/mrdocs/Metadata.hpp b/include/mrdocs/Metadata.hpp index 96f133c7bf..27420f4271 100644 --- a/include/mrdocs/Metadata.hpp +++ b/include/mrdocs/Metadata.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/include/mrdocs/Metadata/Symbol/Macro.hpp b/include/mrdocs/Metadata/Symbol/Macro.hpp new file mode 100644 index 0000000000..d525d3ee5d --- /dev/null +++ b/include/mrdocs/Metadata/Symbol/Macro.hpp @@ -0,0 +1,82 @@ +// +// 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) 2026 Gennaro Prota (gennaro.prota@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_API_METADATA_SYMBOL_MACRO_HPP +#define MRDOCS_API_METADATA_SYMBOL_MACRO_HPP + +#include +#include +#include +#include + +namespace mrdocs { + +/** Info for preprocessor macros. + + Covers both object-like and function-like macros. +*/ +struct MacroSymbol final + : SymbolCommonBase +{ + /** Whether this is a function-like macro. + */ + bool IsFunctionLike = false; + + /** The names of the macro's parameters. + + Empty for object-like macros. For variadic + function-like macros, this lists only the + named parameters; variadicness is indicated + by @ref IsVariadic. + */ + std::vector Parameters; + + /** Whether the macro takes a variadic argument list. + + True for macros declared with `...`, such as + `#define LOG(fmt, ...) ...`. + */ + bool IsVariadic = false; + + /** The full source of the macro definition. + + Captured from the start of the line containing + the `#define` directive through the end of the + macro definition, line continuations and all, + with one normalization: whitespace between `#` + and `define` (typically used when the macro + definition is in a `#if`/`#ifdef`/`#ifndef`), + and the matching leading whitespace on + continuation lines, is stripped; so the + definition in the synopsis reads as a + top-level directive without a surrounding + `#if`/`#ifdef`/`#ifndef`. + */ + std::string Source; + + //-------------------------------------------- + + /** Create a macro symbol bound to an ID. + */ + explicit MacroSymbol(SymbolID const& ID) noexcept + : SymbolCommonBase(ID) + { + } +}; + +MRDOCS_DESCRIBE_STRUCT( + MacroSymbol, + (SymbolCommonBase), + (IsFunctionLike, Parameters, IsVariadic, Source) +) + +} // mrdocs + +#endif // MRDOCS_API_METADATA_SYMBOL_MACRO_HPP diff --git a/include/mrdocs/Metadata/Symbol/SymbolKind.hpp b/include/mrdocs/Metadata/Symbol/SymbolKind.hpp index c1ac7b1327..a7f7dcc3ac 100644 --- a/include/mrdocs/Metadata/Symbol/SymbolKind.hpp +++ b/include/mrdocs/Metadata/Symbol/SymbolKind.hpp @@ -30,7 +30,7 @@ enum class SymbolKind { MRDOCS_DESCRIBE_ENUM(SymbolKind, Namespace, Record, Function, Overloads, Enum, EnumConstant, Typedef, Variable, Guide, - NamespaceAlias, Using, Concept) + NamespaceAlias, Using, Concept, Macro) /** Count the number of SymbolKind enumerators. @return Number of `SymbolKind` values generated from SymbolNodes.inc. diff --git a/include/mrdocs/Metadata/Symbol/SymbolNodes.inc b/include/mrdocs/Metadata/Symbol/SymbolNodes.inc index 64866eb9d8..31fae3ca9f 100644 --- a/include/mrdocs/Metadata/Symbol/SymbolNodes.inc +++ b/include/mrdocs/Metadata/Symbol/SymbolNodes.inc @@ -24,6 +24,7 @@ INFO(Guide) INFO(NamespaceAlias) INFO(Using) INFO(Concept) +INFO(Macro) #undef INFO diff --git a/mrdocs.rnc b/mrdocs.rnc index 776acb447b..14d4be41bf 100644 --- a/mrdocs.rnc +++ b/mrdocs.rnc @@ -555,6 +555,18 @@ grammar #--------------------------------------------- + Macro = + element macro + { + SymbolBase, + element is-function-like { Bool }?, + element parameters { text }*, + element is-variadic { Bool }?, + element source { text }? + } + + #--------------------------------------------- + Overloads = element overloads { @@ -582,6 +594,7 @@ grammar NamespaceAlias | Using | Concept | + Macro | Overloads ) diff --git a/share/mrdocs/addons/generator/adoc/layouts/macros-index.adoc.hbs b/share/mrdocs/addons/generator/adoc/layouts/macros-index.adoc.hbs new file mode 100644 index 0000000000..0c82eeee56 --- /dev/null +++ b/share/mrdocs/addons/generator/adoc/layouts/macros-index.adoc.hbs @@ -0,0 +1,6 @@ +{{!-- + Layout for the corpus-level Macros index page (multipage mode). + The wrapper template handles the title, footer, etc.; this + template only emits the page contents. +--}} +{{>symbol/detail/members-table-impl members=corpusMacros isName=false includeBrief=true title=""}} diff --git a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs index 1f3290f23f..a0762569a5 100644 --- a/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs +++ b/share/mrdocs/addons/generator/adoc/partials/symbol.adoc.hbs @@ -86,6 +86,12 @@ {{! Namespace members }} {{else if (eq symbol.kind "namespace")}} {{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} +{{! Navigation hint: macros live on their own page since they + are not in any C++ scope. }} +{{#if (and @root.config.multipage (not symbol.parent) @root.corpusMacros)}} + +See also: <> +{{/if}} {{! Enum members }} {{else if (eq symbol.kind "enum")}} {{>symbol/members-table members=symbol.constants title="Members"}} diff --git a/share/mrdocs/addons/generator/common/partials/symbol/signature/macro.hbs b/share/mrdocs/addons/generator/common/partials/symbol/signature/macro.hbs new file mode 100644 index 0000000000..844cec1769 --- /dev/null +++ b/share/mrdocs/addons/generator/common/partials/symbol/signature/macro.hbs @@ -0,0 +1,18 @@ +{{!-- + Render the synopsis for a preprocessor macro. + + For regular macros, the synopsis is the normalized + source of the `#define` directive. + + For see-below macros, the body is replaced by a + `/* see-below */` marker. Implementation-defined + macros are not rendered at all (see + `shouldGenerate` in VisitorHelpers.cpp). + + Expected Context: {Symbol Object} (kind == "macro") +--}} +{{#isSeeBelow~}} +#define {{name}}{{#if isFunctionLike}}({{#each parameters}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}{{#if isVariadic}}{{#if parameters}}, {{/if}}...{{/if}}){{/if}} /* see-below */ +{{~else~}} +{{ source }} +{{~/isSeeBelow}} diff --git a/share/mrdocs/addons/generator/html/layouts/macros-index.html.hbs b/share/mrdocs/addons/generator/html/layouts/macros-index.html.hbs new file mode 100644 index 0000000000..9c06a39048 --- /dev/null +++ b/share/mrdocs/addons/generator/html/layouts/macros-index.html.hbs @@ -0,0 +1,6 @@ +{{!-- + Layout for the corpus-level Macros index page (multipage mode). + The wrapper template handles , , footer, etc.; this + template only emits the page contents. +--}} +{{>symbol/detail/members-table-impl members=corpusMacros isName=false includeBrief=true title=""}} diff --git a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs index acab90eb12..2236f5f499 100644 --- a/share/mrdocs/addons/generator/html/partials/symbol.html.hbs +++ b/share/mrdocs/addons/generator/html/partials/symbol.html.hbs @@ -110,6 +110,11 @@ {{! Namespace members }} {{else if (eq symbol.kind "namespace")}} {{>symbol/tranche tranche=symbol.members label="" is-namespace=true}} +{{! Navigation hint: macros live on their own page since they + are not in any C++ scope. }} +{{#if (and @root.config.multipage (not symbol.parent) @root.corpusMacros)}} +

See also: Macros

+{{/if}} {{! Enum members }} {{else if (eq symbol.kind "enum")}} {{>symbol/members-table members=symbol.constants title="Members"}} diff --git a/src/lib/AST/ASTAction.cpp b/src/lib/AST/ASTAction.cpp index 0dfa6a68a3..9b4f3793aa 100644 --- a/src/lib/AST/ASTAction.cpp +++ b/src/lib/AST/ASTAction.cpp @@ -7,16 +7,20 @@ // 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) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // #include #include +#include #include #include +#include #include #include +#include namespace mrdocs { @@ -31,6 +35,13 @@ ExecuteAction() return; } + // Register the macro collector before parsing begins. + // The preprocessor takes ownership; the records are + // pushed into `macroDefs_`, owned by this action. + CI.getPreprocessor().addPPCallbacks( + std::make_unique( + macroDefs_, CI.getPreprocessor())); + // Ensure comments in system headers are retained. // We may want them if, e.g., a declaration was extracted // as a dependency @@ -96,7 +107,7 @@ CreateASTConsumer( llvm::StringRef) { return std::make_unique( - config_, ex_, Compiler); + config_, ex_, Compiler, macroDefs_); } } // mrdocs diff --git a/src/lib/AST/ASTAction.hpp b/src/lib/AST/ASTAction.hpp index 2063c4b24f..52c2f0ab01 100644 --- a/src/lib/AST/ASTAction.hpp +++ b/src/lib/AST/ASTAction.hpp @@ -7,6 +7,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) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -15,10 +16,12 @@ #define MRDOCS_LIB_AST_ASTACTION_HPP #include +#include #include #include #include #include +#include namespace mrdocs { @@ -43,6 +46,7 @@ class ASTAction ExecutionContext& ex_; ConfigImpl const& config_; MissingSymbolSink* missingSink_ = nullptr; + std::vector macroDefs_; public: ASTAction( diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 4b935943f7..bcf404467b 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -7,6 +7,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) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -26,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -38,6 +41,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -51,13 +58,15 @@ ASTVisitor( Diagnostics const& diags, clang::CompilerInstance& compiler, clang::ASTContext& context, - clang::Sema& sema) noexcept + clang::Sema& sema, + std::vector& macroDefs) noexcept : config_(config) , diags_(diags) , compiler_(compiler) , context_(context) , source_(context.getSourceManager()) , sema_(sema) + , macroDefs_(macroDefs) { // Install handlers for our custom commands initCustomCommentCommands(context_); @@ -81,6 +90,183 @@ build() clang::TranslationUnitDecl const* TU = context_.getTranslationUnitDecl(); traverse(TU); MRDOCS_ASSERT(find(SymbolID::global)); + addMacros(); +} + +void +ASTVisitor:: +addMacros() +{ + for (MacroDefinition const& m : macroDefs_) + { + // `findFileInfo` returns null only for invalid `SourceLocation` + // values and a handful of exotic `FileID` kinds (predefined-macro + // buffers, unusual `#line` situations). `MacroCollector` already + // drops invalid locations, builtins, and system headers, so + // anything reaching here points at a real source file. + FileInfo* file = findFileInfo(m.DefLoc); + MRDOCS_ASSERT(file); + + clang::PresumedLoc const presLoc = + source_.getPresumedLoc(m.DefLoc, false); + MRDOCS_CHECK_OR_CONTINUE(presLoc.isValid()); + + // Stable `SymbolID` derived from the source-relative + // path, line number, and macro name. Two definitions + // with the same name on the same line of the same + // file collapse to a single symbol; that matches how + // a single header is treated when included from + // multiple TUs. + std::string const usr = std::format( + "macro:{}@{}:{}", + m.Name, + file->short_path, + presLoc.getLine()); + std::array const h = + llvm::SHA1::hash(llvm::arrayRefFromStringRef(usr)); + SymbolID const id(h.data()); + MRDOCS_CHECK_OR_CONTINUE(info_.find(id) == info_.end()); + + // Apply user-configured filters: by name (symbol + // patterns) and by source location (file patterns). + ExtractionMode const mode = macroNameMode(m.Name); + MRDOCS_CHECK_OR_CONTINUE(mode != ExtractionMode::Dependency); + MRDOCS_CHECK_OR_CONTINUE(checkFileFilters(file->full_path)); + + std::unique_ptr sym = + std::make_unique(id); + sym->Name = m.Name; + sym->IsFunctionLike = !m.IsObjectLike; + sym->IsVariadic = m.IsVariadic; + sym->Parameters = m.Parameters; + sym->Source = m.Source; + sym->Parent = SymbolID::invalid; + sym->Extraction = mode; + + // Associate any doc comment immediately preceding the + // directive. Clang doesn't link comments to macros, so + // we look one up by source location. + if (clang::RawComment const* RC = + findPrecedingMacroComment(m.DefLoc)) + { + clang::comments::FullComment* FC = RC->parse( + context_, &sema_.getPreprocessor(), nullptr); + if (FC) + { + populateDocComment( + sym->doc, FC, context_, config_, diags_); + } + } + + // When `extract-all` is off, require a doc comment. + // Undocumented macros are dropped entirely (we don't + // bother tracking them for the warn-if-undocumented + // pass since they have no Decl-based identity). + MRDOCS_CHECK_OR_CONTINUE(config_->extractAll || sym->doc); + + sym->Loc.DefLoc = Location( + file->full_path, + file->short_path, + file->source_path, + presLoc.getLine(), + presLoc.getColumn(), + sym->doc.has_value()); + + info_.emplace(std::move(sym)); + } +} + +clang::RawComment const* +ASTVisitor:: +findPrecedingMacroComment(clang::SourceLocation defLoc) const +{ + clang::FileID const defFile = source_.getFileID(defLoc); + std::map const* const inFile = + context_.Comments.getCommentsInFile(defFile); + if (!inFile) + { + return nullptr; + } + + // The map is keyed by start offset, ascending. The latest + // comment whose start is strictly before `defLoc` is our + // candidate. + unsigned const defOffset = source_.getFileOffset(defLoc); + auto it = inFile->lower_bound(defOffset); + if (it == inFile->begin()) + { + return nullptr; + } + --it; + clang::RawComment const* const candidate = it->second; + if (!candidate || !candidate->isDocumentation()) + { + return nullptr; + } + + // Proximity: lines between the comment and the directive + // must be blank. Otherwise the comment belongs to whatever + // is in between, not to this macro. + unsigned const commentEndLine = + source_.getSpellingLineNumber(candidate->getEndLoc()); + unsigned const directiveLine = + source_.getSpellingLineNumber(defLoc); + if (directiveLine <= commentEndLine) + { + return nullptr; + } + clang::LangOptions const& langOpts = context_.getLangOpts(); + for (unsigned line = commentEndLine + 1; + line < directiveLine; + ++line) + { + clang::SourceLocation const lineStart = + source_.translateLineCol(defFile, line, 1); + clang::SourceLocation const nextLineStart = + source_.translateLineCol(defFile, line + 1, 1); + std::string const text = clang::Lexer::getSourceText( + clang::CharSourceRange::getCharRange( + lineStart, nextLineStart), + source_, langOpts).str(); + for (char const c : text) + { + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') + { + return nullptr; + } + } + } + return candidate; +} + +ExtractionMode +ASTVisitor:: +macroNameMode(std::string_view name) const +{ + using enum SymbolCheckType; + auto const matches = [&]( + std::vector const& p) + { + return checkSymbolFiltersImpl(p, name); + }; + if (matches(config_->excludeSymbols)) + { + return ExtractionMode::Dependency; + } + if (matches(config_->implementationDefined)) + { + return ExtractionMode::ImplementationDefined; + } + if (matches(config_->seeBelow)) + { + return ExtractionMode::SeeBelow; + } + if (!config_->includeSymbols.empty() && + !matches(config_->includeSymbols)) + { + return ExtractionMode::Dependency; + } + return ExtractionMode::Regular; } template < @@ -590,7 +776,7 @@ populate( clang::comments::FullComment* FC = RC->parse(D->getASTContext(), &sema_.getPreprocessor(), D); MRDOCS_CHECK_OR(FC, false); - populateDocComment(doc, FC, D, config_, diags_); + populateDocComment(doc, FC, D->getASTContext(), config_, diags_); return true; } diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index 4fa15dfaf1..8f8c0570f5 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -7,6 +7,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) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -15,6 +16,7 @@ #define MRDOCS_LIB_AST_ASTVISITOR_HPP #include +#include #include #include #include @@ -24,6 +26,7 @@ #include #include #include +#include namespace mrdocs { @@ -75,6 +78,10 @@ class ASTVisitor // Semantic analysis clang::Sema& sema_; + // Macros captured by `MacroCollector` during preprocessing. + // Owned by `ASTAction`. + std::vector& macroDefs_; + // An unordered set of all extracted Info declarations SymbolSet info_; @@ -282,13 +289,15 @@ class ASTVisitor @param compiler The compiler instance. @param context The AST context. @param sema The clang::Sema object. + @param macroDefs The macro definitions captured by MacroCollector. */ ASTVisitor( ConfigImpl const& config, Diagnostics const& diags, clang::CompilerInstance& compiler, clang::ASTContext& context, - clang::Sema& sema) noexcept; + clang::Sema& sema, + std::vector& macroDefs) noexcept; /** Build the metadata representation from the AST. @@ -340,6 +349,33 @@ class ASTVisitor } private: + /* Drain captured macro records into `info_`. + + Called at the end of `build()`. Each record becomes a + `MacroSymbol` with `Parent = SymbolID::invalid` (macros + live at the corpus root, not under any namespace) and + a `SymbolID` derived from the macro's name and + canonical source location. + */ + void + addMacros(); + + /* Find a doc comment immediately preceding a `#define` + directive at `defLoc`. Returns `nullptr` if no + documentation-style comment is adjacent (lines between + the comment and the directive must be blank). + */ + clang::RawComment const* + findPrecedingMacroComment(clang::SourceLocation defLoc) const; + + /* Apply the configured symbol-pattern filters to a macro + name and return the corresponding extraction mode. + `Dependency` means "filter out". Mirrors the pattern + precedence used for declarations. + */ + ExtractionMode + macroNameMode(std::string_view name) const; + // ================================================= // AST Traversal // ================================================= diff --git a/src/lib/AST/ASTVisitorConsumer.cpp b/src/lib/AST/ASTVisitorConsumer.cpp index dd96ce1ee0..ed70490b31 100644 --- a/src/lib/AST/ASTVisitorConsumer.cpp +++ b/src/lib/AST/ASTVisitorConsumer.cpp @@ -28,7 +28,8 @@ HandleTranslationUnit(clang::ASTContext& Context) diags, compiler_, Context, - *sema_); + *sema_, + macroDefs_); visitor.build(); ex_.report(std::move(visitor.results()), std::move(diags), std::move(visitor.undocumented())); } diff --git a/src/lib/AST/ASTVisitorConsumer.hpp b/src/lib/AST/ASTVisitorConsumer.hpp index ed13878725..012ee3bb39 100644 --- a/src/lib/AST/ASTVisitorConsumer.hpp +++ b/src/lib/AST/ASTVisitorConsumer.hpp @@ -15,10 +15,12 @@ #define MRDOCS_LIB_AST_ASTVISITORCONSUMER_HPP #include +#include #include #include #include #include +#include namespace mrdocs { @@ -42,16 +44,19 @@ class ASTVisitorConsumer ConfigImpl const& config_; ExecutionContext& ex_; clang::CompilerInstance& compiler_; + std::vector& macroDefs_; clang::Sema* sema_ = nullptr; public: ASTVisitorConsumer( ConfigImpl const& config, ExecutionContext& ex, - clang::CompilerInstance& compiler) noexcept + clang::CompilerInstance& compiler, + std::vector& macroDefs) noexcept : config_(config) , ex_(ex) , compiler_(compiler) + , macroDefs_(macroDefs) { } diff --git a/src/lib/AST/ExtractDocComment.cpp b/src/lib/AST/ExtractDocComment.cpp index 72961c8927..e9f8bddafe 100644 --- a/src/lib/AST/ExtractDocComment.cpp +++ b/src/lib/AST/ExtractDocComment.cpp @@ -1596,11 +1596,11 @@ class DocCommentVisitor public: DocCommentVisitor( clang::comments::FullComment const* FC, - clang::Decl const* D, + clang::ASTContext const& ctx, Config const& config, Diagnostics& diags) : config_(config) - , ctx_(D->getASTContext()) + , ctx_(ctx) , sm_(ctx_.getSourceManager()) , FC_(FC) , diags_(diags) @@ -1636,12 +1636,12 @@ void populateDocComment( Optional& jd, clang::comments::FullComment const* FC, - clang::Decl const* D, + clang::ASTContext const& ctx, Config const& config, Diagnostics& diags) { - MRDOCS_COMMENT_TRACE(FC, D->getASTContext()); - DocCommentVisitor visitor(FC, D, config, diags); + MRDOCS_COMMENT_TRACE(FC, ctx); + DocCommentVisitor visitor(FC, ctx, config, diags); auto result = visitor.build(); if (!result.empty()) { diff --git a/src/lib/AST/ExtractDocComment.hpp b/src/lib/AST/ExtractDocComment.hpp index 627efeb40c..4a12486a8a 100644 --- a/src/lib/AST/ExtractDocComment.hpp +++ b/src/lib/AST/ExtractDocComment.hpp @@ -18,7 +18,6 @@ #include namespace clang { -class Decl; class ASTContext; class RawComment; namespace comments { @@ -37,14 +36,16 @@ void initCustomCommentCommands( clang::ASTContext& ctx); -/** Extract doc comments from a declaration +/** Extract doc comments from a parsed FullComment. - Extract the DocComment from a declaration, populating the - DocComment object with the information parsed by clang. + Populate the DocComment object with the information + parsed by Clang. Used both for declaration comments + (caller passes `someDecl->getASTContext()`) and for + macro comments, where there is no `Decl`. @param jd The DocComment object to populate @param FC The full comment to parse - @param D The declaration to which the comment applies + @param ctx The ASTContext owning the comment @param config The MrDocs configuration object @param diags The diagnostics object */ @@ -52,7 +53,7 @@ void populateDocComment( Optional& jd, clang::comments::FullComment const* FC, - clang::Decl const* D, + clang::ASTContext const& ctx, Config const& config, Diagnostics& diags); diff --git a/src/lib/AST/MacroCollector.cpp b/src/lib/AST/MacroCollector.cpp new file mode 100644 index 0000000000..d399d15088 --- /dev/null +++ b/src/lib/AST/MacroCollector.cpp @@ -0,0 +1,197 @@ +// +// 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) 2026 Gennaro Prota (gennaro.prota@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrdocs { + +namespace { + +bool +inSyntheticBuffer( + clang::SourceLocation loc, + clang::SourceManager const& SM) +{ + // `` holds target predefines (`_MT`, `_MSC_VER`, ...); + // `` holds `-D` macros from the command line + // (e.g. mrdocs's own `__MRDOCS__`). Neither is user source. + return SM.isInSystemHeader(loc) || + SM.isWrittenInBuiltinFile(loc) || + SM.isWrittenInCommandLineFile(loc); +} + +bool +shouldSkip( + clang::MacroInfo const* MI, + clang::SourceLocation defLoc, + clang::SourceManager const& SM) +{ + if (!MI || MI->isBuiltinMacro() || defLoc.isInvalid()) + { + return true; + } + return inSyntheticBuffer(defLoc, SM); +} + +std::vector +gatherParameters(clang::MacroInfo const* MI) +{ + std::vector result; + // For variadic macros, Clang appends a synthetic + // `__VA_ARGS__` identifier as the last entry of + // `MI->params()`. Drop it: variadicness is reported + // via `MacroDefinition::IsVariadic`, mirroring how + // `FunctionSymbol::Params` excludes the trailing + // C-style `...` and uses `IsVariadic` for the same + // information. + for (clang::IdentifierInfo const* P : MI->params()) + { + if (P && P->getName() != "__VA_ARGS__") + { + result.emplace_back(P->getName().str()); + } + } + return result; +} + +/* Strip whitespace between `#` and `define` from a directive's + first line, and the same number of leading whitespace chars + from each continuation line so that trailing backslashes stay + column-aligned. + + A continuation line that starts with fewer whitespace chars + than the count being stripped keeps whatever leading + whitespace it has, so we never eat actual content. +*/ +std::string +normalizeDefineDirective(std::string source) +{ + std::size_t const hashPos = source.find('#'); + if (hashPos == std::string::npos) + { + return source; + } + std::size_t const definePos = source.find("define", hashPos + 1); + if (definePos == std::string::npos) + { + return source; + } + for (std::size_t i = hashPos + 1; i < definePos; ++i) + { + if (source[i] != ' ' && source[i] != '\t') + { + return source; + } + } + std::size_t const stripCount = definePos - hashPos - 1; + if (stripCount == 0) + { + return source; + } + source.erase(hashPos + 1, stripCount); + std::size_t pos = source.find('\n'); + while (pos != std::string::npos) + { + std::size_t const lineStart = pos + 1; + std::size_t available = 0; + while (available < stripCount && + lineStart + available < source.size() && + (source[lineStart + available] == ' ' || + source[lineStart + available] == '\t')) + { + ++available; + } + if (available > 0) + { + source.erase(lineStart, available); + } + pos = source.find('\n', lineStart); + } + return source; +} + +/* Verbatim source from the start of the line containing the + macro definition through the end of the macro definition, + with Boost.Config-style `# define` whitespace normalized + away. Renderers use this as the synopsis so the user's + layout (line continuations, body indentation) is preserved + while orphaned preprocessor indentation is removed. +*/ +std::string +extractSource( + clang::MacroInfo const* MI, + clang::SourceManager const& SM, + clang::LangOptions const& LO) +{ + clang::SourceLocation const defLoc = MI->getDefinitionLoc(); + clang::FileID const fileId = SM.getFileID(defLoc); + unsigned const line = SM.getSpellingLineNumber(defLoc); + clang::SourceLocation const lineStart = + SM.translateLineCol(fileId, line, 1); + // `getDefinitionEndLoc()` returns the start of the last + // token (not past it), so `getTokenRange` is required to + // include it in the extracted text. + std::string const raw = clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange( + lineStart, MI->getDefinitionEndLoc()), + SM, LO).str(); + return normalizeDefineDirective(raw); +} + +MacroDefinition +buildDefinition( + clang::MacroInfo const* MI, + clang::IdentifierInfo const* II, + clang::SourceLocation defLoc, + clang::SourceManager const& SM, + clang::LangOptions const& LO) +{ + MacroDefinition m; + m.Name = II->getName().str(); + m.DefLoc = defLoc; + m.IsObjectLike = MI->isObjectLike(); + m.IsVariadic = MI->isVariadic(); + m.Parameters = gatherParameters(MI); + m.Source = extractSource(MI, SM, LO); + return m; +} + +} // unnamed namespace + +void +MacroCollector:: +MacroDefined( + clang::Token const& MacroNameTok, + clang::MacroDirective const* MD) +{ + if (!MD) + { + return; + } + clang::MacroInfo const* const MI = MD->getMacroInfo(); + clang::SourceLocation const defLoc = MI ? + MI->getDefinitionLoc() : clang::SourceLocation(); + clang::SourceManager const& SM = pp_.getSourceManager(); + clang::IdentifierInfo const* const II = MacroNameTok.getIdentifierInfo(); + if (!II || shouldSkip(MI, defLoc, SM)) + { + return; + } + sink_.push_back(buildDefinition(MI, II, defLoc, SM, pp_.getLangOpts())); +} + +} // mrdocs diff --git a/src/lib/AST/MacroCollector.hpp b/src/lib/AST/MacroCollector.hpp new file mode 100644 index 0000000000..10bbbf633c --- /dev/null +++ b/src/lib/AST/MacroCollector.hpp @@ -0,0 +1,91 @@ +// +// 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) 2026 Gennaro Prota (gennaro.prota@gmail.com) +// +// Official repository: https://github.com/cppalliance/mrdocs +// + +#ifndef MRDOCS_LIB_AST_MACROCOLLECTOR_HPP +#define MRDOCS_LIB_AST_MACROCOLLECTOR_HPP + +#include +#include +#include +#include + +namespace clang { +class MacroDirective; +class Preprocessor; +class Token; +} + +namespace mrdocs { + +/** A captured `#define` from the preprocessor. + + Filled in by @ref MacroCollector when the preprocessor + sees a macro definition. The visitor consumes these + at the end of the translation unit and turns them into + @ref MacroSymbol instances. +*/ +struct MacroDefinition { + /** The macro identifier. + */ + std::string Name; + + /** The location of the macro name in the `#define`. + */ + clang::SourceLocation DefLoc; + + /** True for object-like macros (no parameter list). + */ + bool IsObjectLike = true; + + /** True when the macro takes a variadic argument list. + */ + bool IsVariadic = false; + + /** Names of the named parameters, in declaration order. + */ + std::vector Parameters; + + /** Full source of the macro definition, line continuations + and all. Used as the synopsis at render time. + */ + std::string Source; +}; + +/** Capture `MacroDefined` events from the preprocessor. + + Skip definitions in system headers and built-in macros; + the visitor applies any further filtering. +*/ +class MacroCollector final : public clang::PPCallbacks { + std::vector& sink_; + clang::Preprocessor const& pp_; + +public: + /** Construct a collector that pushes into the given sink. + */ + MacroCollector( + std::vector& sink, + clang::Preprocessor const& pp) noexcept + : sink_(sink) + , pp_(pp) + { + } + + /** Record a `#define` directive. + */ + void + MacroDefined( + clang::Token const& MacroNameTok, + clang::MacroDirective const* MD) override; +}; + +} // mrdocs + +#endif // MRDOCS_LIB_AST_MACROCOLLECTOR_HPP diff --git a/src/lib/Corpus.cpp b/src/lib/Corpus.cpp index cf8ed6ab32..acb1b33ba5 100644 --- a/src/lib/Corpus.cpp +++ b/src/lib/Corpus.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -13,6 +14,7 @@ #include #include #include +#include #include namespace mrdocs { @@ -43,6 +45,46 @@ globalNamespace() const noexcept return get(SymbolID::global); } +std::vector +Corpus:: +macros() const +{ + std::vector result; + for (Symbol const& s : *this) + { + if (MacroSymbol const* m = s.asMacroPtr()) + { + result.push_back(m); + } + } + std::ranges::sort( + result, + [](MacroSymbol const* a, MacroSymbol const* b) + { + if (std::strong_ordering const cmp = a->Name <=> b->Name; + !std::is_eq(cmp)) + { + return std::is_lt(cmp); + } + // Tiebreak by source location, so order stays + // stable when the same name appears in multiple + // files. + Optional const& aLoc = a->Loc.DefLoc; + Optional const& bLoc = b->Loc.DefLoc; + if (!aLoc || !bLoc) + { + return aLoc.has_value() < bLoc.has_value(); + } + if (std::strong_ordering const cmp = aLoc->FullPath <=> bLoc->FullPath; + !std::is_eq(cmp)) + { + return std::is_lt(cmp); + } + return aLoc->LineNumber < bLoc->LineNumber; + }); + return result; +} + //------------------------------------------------ // // Modifiers diff --git a/src/lib/Gen/adoc/AdocGenerator.cpp b/src/lib/Gen/adoc/AdocGenerator.cpp index 3a74512a4b..99e26e92f3 100644 --- a/src/lib/Gen/adoc/AdocGenerator.cpp +++ b/src/lib/Gen/adoc/AdocGenerator.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -23,6 +24,13 @@ escape(OutputRef& os, std::string_view const str) const AdocEscape(os, str); } +std::string +AdocGenerator:: +macrosSectionHeading() const +{ + return "\n[#macros]\n== Macros\n\n"; +} + } // adoc std::unique_ptr diff --git a/src/lib/Gen/adoc/AdocGenerator.hpp b/src/lib/Gen/adoc/AdocGenerator.hpp index 382e231c50..e2bb0d56f0 100644 --- a/src/lib/Gen/adoc/AdocGenerator.hpp +++ b/src/lib/Gen/adoc/AdocGenerator.hpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -44,6 +45,10 @@ class AdocGenerator final void escape(OutputRef& os, std::string_view str) const override; + +protected: + std::string + macrosSectionHeading() const override; }; } // mrdocs::adoc diff --git a/src/lib/Gen/hbs/Builder.cpp b/src/lib/Gen/hbs/Builder.cpp index feb1a90925..9616eb396a 100644 --- a/src/lib/Gen/hbs/Builder.cpp +++ b/src/lib/Gen/hbs/Builder.cpp @@ -365,7 +365,10 @@ loadLayoutTemplate( loaded = true; } if (!loaded) - formatError("Template {} not found in addons search path", filename).Throw(); + { + return Unexpected(formatError( + "Template {} not found in addons search path", filename)); + } return {}; } @@ -399,6 +402,13 @@ Builder( exp.error().Throw(); if (auto exp = loadLayoutTemplate(templates_, layoutDirs, std::format("wrapper.{}.hbs", domCorpus.fileExtension)); !exp) exp.error().Throw(); + // The macros-index layout is only used when the corpus has + // macros, and not every addon directory bundles it (some + // tests carry a minimal layout subset). Load it best-effort + // and let `renderMacrosIndexPage` fail if it's actually + // needed but missing. + (void)loadLayoutTemplate(templates_, layoutDirs, + std::format("macros-index.{}.hbs", domCorpus.fileExtension)); } //------------------------------------------------ @@ -459,6 +469,24 @@ createContext(Symbol const& I) ctx.set("page", page); ctx.set("symbol", domCorpus.get(I.id)); ctx.set("config", domCorpus->config.object()); + // Macros live at the corpus root, not under any namespace, + // so the namespace template can't reach them through + // `symbol.members`. Expose them at `@root.corpusMacros` so + // the global-namespace page can link to them. Only set the + // field when there is at least one macro: MrDocs's + // Handlebars treats an empty array as truthy, which would + // otherwise inject the "See also" hint on every page. + std::vector const macroList = + domCorpus.getCorpus().macros(); + if (!macroList.empty()) + { + dom::Array macros; + for (MacroSymbol const* m : macroList) + { + macros.emplace_back(domCorpus.construct(*m)); + } + ctx.set("corpusMacros", macros); + } return ctx; } @@ -536,6 +564,59 @@ wrapperTemplateFile() const return std::format("wrapper.{}.hbs", domCorpus.fileExtension); } +Expected +Builder:: +renderMacrosIndexPage(std::ostream& os) +{ + // Build a context that mirrors what `createContext(Symbol)` + // would produce, but with a synthesized `symbol` standing in + // for the missing C++ scope: name "Macros", no parent, no + // anchor target. The wrapper template walks + // `symbol/qualified-name-title` for the page heading, which + // for this stub falls through to `symbol/name-text` and just + // emits "Macros". + dom::Object ctx; + + dom::Object page; + page.set("stylesheets", domCorpus.stylesheets); + page.set("inlineStyles", domCorpus.inlineStyles); + page.set("inlineScripts", domCorpus.inlineScripts); + page.set("hasDefaultStyles", domCorpus.hasDefaultStyles); + page.set("relfileprefix", std::string{}); + ctx.set("page", page); + + dom::Object stub; + stub.set("name", std::string("Macros")); + stub.set("kind", std::string{}); + stub.set("anchor", std::string("macros")); + // `relativize` uses `@root.symbol.url` as the source path + // when computing relative hrefs; without it, links in the + // macros table render as absolute (e.g. `/MY_INC.html`), + // which breaks file:// browsing and any sub-path hosting. + stub.set("url", std::format("/macros.{}", domCorpus.fileExtension)); + ctx.set("symbol", stub); + ctx.set("config", domCorpus->config.object()); + + dom::Array macros; + for (MacroSymbol const* m : domCorpus.getCorpus().macros()) + { + macros.emplace_back(domCorpus.construct(*m)); + } + ctx.set("corpusMacros", macros); + + std::string const macrosTemplate = + std::format("macros-index.{}.hbs", domCorpus.fileExtension); + dom::Object const wrapperCtx = createFrame(ctx); + wrapperCtx.set("contents", dom::makeInvocable( + [this, macrosTemplate, ctx, &os]( + dom::Value const&) -> Expected + { + MRDOCS_TRY(callTemplate(os, macrosTemplate, ctx)); + return {}; + })); + return callTemplate(os, wrapperTemplateFile(), wrapperCtx); +} + } // hbs } // mrdocs diff --git a/src/lib/Gen/hbs/Builder.hpp b/src/lib/Gen/hbs/Builder.hpp index 67e277100d..fcb43d9d00 100644 --- a/src/lib/Gen/hbs/Builder.hpp +++ b/src/lib/Gen/hbs/Builder.hpp @@ -107,6 +107,18 @@ class Builder std::ostream& os, std::function()> contentsCb); + /** Render the corpus-level Macros index page. + + Used in multi-page mode to produce a stand-alone + `macros.{ext}` page listing all macros, since macros + live at the corpus root rather than under any namespace. + + @param os Stream to receive the rendered page. + @return Success or a template-rendering error. + */ + Expected + renderMacrosIndexPage(std::ostream& os); + private: /** Path to the index template file resolved for the active generator. */ std::string diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.cpp b/src/lib/Gen/hbs/HandlebarsGenerator.cpp index f749b50681..1daa7a63e4 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.cpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.cpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -125,12 +126,48 @@ build( // Visit the corpus MultiPageVisitor visitor(ex, outputPath, corpus); visitor(corpus.globalNamespace()); + // Macros are corpus-level: they live alongside the + // global namespace, not under it. + for (MacroSymbol const* m : corpus.macros()) + { + visitor(*m); + } // Wait for all executors to finish and check errors auto errors = ex.wait(); MRDOCS_CHECK_OR(errors.empty(), Unexpected(errors)); report::info("Generated {} pages", visitor.count()); + // Render the corpus-level Macros index page (multi-page + // mode only). Macros aren't members of any C++ scope, so + // they get their own page rather than being listed under + // the global namespace. + if (!corpus.macros().empty()) + { + std::string const path = files::appendPath( + outputPath, std::format("macros.{}", fileExtension())); + std::ofstream os(path, + std::ios_base::binary | + std::ios_base::out | + std::ios_base::trunc); + if (!os.is_open()) + { + return Unexpected(formatError( + R"(std::ofstream("{}") failed)", path)); + } + ex.async([&os](Builder& builder) + { + if (auto r = builder.renderMacrosIndexPage(os); !r) + { + r.error().Throw(); + } + }); + if (auto extraErrors = ex.wait(); !extraErrors.empty()) + { + return Unexpected(extraErrors); + } + } + MRDOCS_CHECK_OR(!corpus.config->tagfile.empty(), {}); MRDOCS_TRY(buildTagfile(corpus.config->tagfile, corpus)); return {}; @@ -215,6 +252,19 @@ buildOne( // Visit the corpus SinglePageVisitor visitor(ex, corpus, os); visitor(corpus.globalNamespace()); + std::vector const macros = corpus.macros(); + if (!macros.empty()) + { + std::string heading = macrosSectionHeading(); + if (!heading.empty()) + { + visitor.writeText(std::move(heading)); + } + } + for (MacroSymbol const* m : macros) + { + visitor(*m); + } // Wait for all executors to finish and check errors auto errors = ex.wait(); @@ -229,6 +279,19 @@ buildOne( // This helper will write contents directly to ostream SinglePageVisitor visitor(ex, corpus, os); visitor(corpus.globalNamespace()); + std::vector const macros = corpus.macros(); + if (!macros.empty()) + { + std::string heading = macrosSectionHeading(); + if (!heading.empty()) + { + visitor.writeText(std::move(heading)); + } + } + for (MacroSymbol const* m : macros) + { + visitor(*m); + } // Wait for all executors to finish and check errors auto errors = ex.wait(); @@ -307,6 +370,13 @@ defaultHighlightScript() const highlightJsCdn); } +std::string +HandlebarsGenerator:: +macrosSectionHeading() const +{ + return {}; +} + /** Checks if a path is a remote URL. @param path The path or URL to check. diff --git a/src/lib/Gen/hbs/HandlebarsGenerator.hpp b/src/lib/Gen/hbs/HandlebarsGenerator.hpp index 3996e34135..062434cc9e 100644 --- a/src/lib/Gen/hbs/HandlebarsGenerator.hpp +++ b/src/lib/Gen/hbs/HandlebarsGenerator.hpp @@ -106,6 +106,15 @@ class HandlebarsGenerator /** Inline script used to load and run highlight.js from a CDN. */ virtual std::string defaultHighlightScript() const; + + /** Section heading emitted before the corpus-level macros list. + + Returned text is written verbatim to the output stream + in single-page mode, between the global namespace and + the macros. Empty by default; subclasses override to + emit format-specific markup. + */ + virtual std::string macrosSectionHeading() const; }; } // hbs diff --git a/src/lib/Gen/hbs/SinglePageVisitor.cpp b/src/lib/Gen/hbs/SinglePageVisitor.cpp index 295d053336..46d99a3987 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.cpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.cpp @@ -5,6 +5,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -12,7 +13,9 @@ #include "SinglePageVisitor.hpp" #include "VisitorHelpers.hpp" #include +#include #include +#include namespace mrdocs::hbs { @@ -46,6 +49,17 @@ operator()(T const& I) #define INFO(T) template void SinglePageVisitor::operator()(T##Symbol const&); #include +void +SinglePageVisitor:: +writeText(std::string text) +{ + std::size_t const symbolIdx = numSymbols_++; + ex_.async([this, symbolIdx, text = std::move(text)](Builder&) + { + writePage(text, symbolIdx); + }); +} + // pageNumber is zero-based void SinglePageVisitor:: diff --git a/src/lib/Gen/hbs/SinglePageVisitor.hpp b/src/lib/Gen/hbs/SinglePageVisitor.hpp index ac52b7ea3a..5e2d044ba5 100644 --- a/src/lib/Gen/hbs/SinglePageVisitor.hpp +++ b/src/lib/Gen/hbs/SinglePageVisitor.hpp @@ -5,6 +5,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -53,6 +54,14 @@ class SinglePageVisitor */ template T> void operator()(T const& I); + + /** Write a literal string at the next position in the page. + + The text is enqueued behind whatever symbols are already + in flight, so the order of `operator()` calls and + `writeText` calls is preserved in the output. + */ + void writeText(std::string text); }; } // mrdocs::hbs diff --git a/src/lib/Gen/html/HTMLGenerator.cpp b/src/lib/Gen/html/HTMLGenerator.cpp index f2cfe4ebc0..dc03bd0b5d 100644 --- a/src/lib/Gen/html/HTMLGenerator.cpp +++ b/src/lib/Gen/html/HTMLGenerator.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -22,6 +23,17 @@ escape(OutputRef& os, std::string_view str) const HTMLEscape(os, str); } +std::string +HTMLGenerator:: +macrosSectionHeading() const +{ + return + "
\n" + "

\n" + "Macros

\n" + "
\n"; +} + } // html //------------------------------------------------ diff --git a/src/lib/Gen/html/HTMLGenerator.hpp b/src/lib/Gen/html/HTMLGenerator.hpp index 5d95944193..52d2ea689b 100644 --- a/src/lib/Gen/html/HTMLGenerator.hpp +++ b/src/lib/Gen/html/HTMLGenerator.hpp @@ -6,6 +6,7 @@ // // Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com) +// Copyright (c) 2026 Gennaro Prota (gennaro.prota@gmail.com) // // Official repository: https://github.com/cppalliance/mrdocs // @@ -43,6 +44,10 @@ class HTMLGenerator final void escape(OutputRef& os, std::string_view str) const override; + +protected: + std::string + macrosSectionHeading() const override; }; } // mrdocs::html diff --git a/src/lib/Gen/xml/XMLWriter.cpp b/src/lib/Gen/xml/XMLWriter.cpp index de1e2c7e4a..d4b01dea5b 100644 --- a/src/lib/Gen/xml/XMLWriter.cpp +++ b/src/lib/Gen/xml/XMLWriter.cpp @@ -76,6 +76,13 @@ XMLWriter::build() << "\n"; (*this)(corpus_.globalNamespace()); + // Macros are corpus-level: they are not in any C++ scope, + // so they appear as siblings of the global namespace rather + // than under it. + for (MacroSymbol const* m : corpus_.macros()) + { + (*this)(*m); + } os_ << "\n"; return {}; } diff --git a/src/lib/Metadata/Finalizers/DocComment/Function.hpp b/src/lib/Metadata/Finalizers/DocComment/Function.hpp index c278a3b7d7..364dfb4d28 100644 --- a/src/lib/Metadata/Finalizers/DocComment/Function.hpp +++ b/src/lib/Metadata/Finalizers/DocComment/Function.hpp @@ -429,7 +429,8 @@ populateFunctionReturns(FunctionSymbol& I, CorpusImpl const& corpus) The doc parameter names can contain a single parameter or a list of parameters separated by commas. This function - returns a list of all parameter names in the doc. + returns a list of all parameter names in the doc. Empty + entries (e.g. from a malformed `@param a,`) are skipped. */ llvm::SmallVector getDocCommentParamNames(DocComment const& doc) @@ -443,7 +444,10 @@ getDocCommentParamNames(DocComment const& doc) { auto const trimmed = trim(std::string_view(paramName.begin(), paramName.end())); - result.emplace_back(trimmed); + if (!trimmed.empty()) + { + result.emplace_back(trimmed); + } } } return result; diff --git a/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp b/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp index 497808085d..44855e674d 100644 --- a/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp +++ b/src/lib/Metadata/Finalizers/DocCommentFinalizer.cpp @@ -1948,8 +1948,14 @@ warnDocErrors() { MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); MRDOCS_CHECK_OR_CONTINUE(I->IsCopyFromInherited == false); - MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); - warnParamErrors(dynamic_cast(*I)); + if (I->isFunction()) + { + warnParamErrors(dynamic_cast(*I)); + } + else if (I->isMacro()) + { + warnParamErrors(dynamic_cast(*I)); + } } } @@ -1995,6 +2001,54 @@ warnParamErrors(FunctionSymbol const& I) } +void +DocCommentFinalizer:: +warnParamErrors(MacroSymbol const& I) +{ + MRDOCS_CHECK_OR(I.doc); + + auto docParamNames = getDocCommentParamNames(*I.doc); + + // Check for duplicate doc parameters + std::ranges::sort(docParamNames); + auto [firstDup, lastUnique] = std::ranges::unique(docParamNames); + auto duplicateParamNames = std::ranges::subrange(firstDup, lastUnique); + auto [firstDupDup, _] = std::ranges::unique(duplicateParamNames); + for (auto const uniqueDuplicateParamNames = std::ranges::subrange(firstDup, firstDupDup); + std::string_view duplicateParamName: uniqueDuplicateParamNames) + { + this->warn( + *getPrimaryLocation(I), + "{}: Duplicate parameter documentation for '{}'", + corpus_.Corpus::qualifiedName(I), + duplicateParamName); + } + docParamNames.erase(lastUnique, docParamNames.end()); + + // Check for documented parameters that don't exist on the macro. + // For variadic macros, accept both `...` and `__VA_ARGS__` as valid + // names for the variadic argument list. + auto isMacroParam = [&](std::string_view const name) + { + if (std::ranges::find(I.Parameters, name) != I.Parameters.end()) + { + return true; + } + return I.IsVariadic && (name == "..." || name == "__VA_ARGS__"); + }; + for (std::string_view docParamName: docParamNames) + { + if (!isMacroParam(docParamName)) + { + this->warn( + *getPrimaryLocation(I), + "{}: Documented parameter '{}' does not exist", + corpus_.Corpus::qualifiedName(I), + docParamName); + } + } +} + void DocCommentFinalizer:: warnNoParamDocs() @@ -2004,9 +2058,15 @@ warnNoParamDocs() { MRDOCS_CHECK_OR_CONTINUE(I->Extraction == ExtractionMode::Regular); MRDOCS_CHECK_OR_CONTINUE(I->IsCopyFromInherited == false); - MRDOCS_CHECK_OR_CONTINUE(I->isFunction()); MRDOCS_CHECK_OR_CONTINUE(I->doc); - warnNoParamDocs(dynamic_cast(*I)); + if (I->isFunction()) + { + warnNoParamDocs(dynamic_cast(*I)); + } + else if (I->isMacro()) + { + warnNoParamDocs(dynamic_cast(*I)); + } } } @@ -2057,6 +2117,29 @@ warnNoParamDocs(FunctionSymbol const& I) } } +void +DocCommentFinalizer:: +warnNoParamDocs(MacroSymbol const& I) +{ + // Only the named parameters are required to be + // documented. The variadic argument list is optional; + // if the user does document it (as `@param ...` or + // `@param __VA_ARGS__`), the chosen name's validity + // is checked in `warnParamErrors`. + auto docParamNames = getDocCommentParamNames(*I.doc); + for (std::string_view paramName : I.Parameters) + { + if (std::ranges::find(docParamNames, paramName) == docParamNames.end()) + { + this->warn( + *getPrimaryLocation(I), + "{}: Missing documentation for parameter '{}'", + corpus_.Corpus::qualifiedName(I), + paramName); + } + } +} + void DocCommentFinalizer:: warnUndocEnumValues() diff --git a/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp b/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp index f203487fe6..4392594b99 100644 --- a/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp +++ b/src/lib/Metadata/Finalizers/DocCommentFinalizer.hpp @@ -312,12 +312,18 @@ class DocCommentFinalizer { void warnParamErrors(FunctionSymbol const& I); + void + warnParamErrors(MacroSymbol const& I); + void warnNoParamDocs(); void warnNoParamDocs(FunctionSymbol const& I); + void + warnNoParamDocs(MacroSymbol const& I); + void warnUndocEnumValues(); diff --git a/test-files/golden-tests/config/exclude-symbols/macros-excluded.adoc b/test-files/golden-tests/config/exclude-symbols/macros-excluded.adoc new file mode 100644 index 0000000000..bdac63027f --- /dev/null +++ b/test-files/golden-tests/config/exclude-symbols/macros-excluded.adoc @@ -0,0 +1,40 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#MYLIB_ASSERT] +== MYLIB_ASSERT + +Public assert, kept. + +=== Synopsis + +Declared in `<macros‐excluded.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) +---- + +[#MYLIB_VERSION] +== MYLIB_VERSION + +Public macro, kept. + +=== Synopsis + +Declared in `<macros‐excluded.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_VERSION 1 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/exclude-symbols/macros-excluded.cpp b/test-files/golden-tests/config/exclude-symbols/macros-excluded.cpp new file mode 100644 index 0000000000..ee8f517a56 --- /dev/null +++ b/test-files/golden-tests/config/exclude-symbols/macros-excluded.cpp @@ -0,0 +1,13 @@ +// Macros matching the exclude pattern are dropped. + +/// Public macro, kept. +#define MYLIB_VERSION 1 + +/// Implementation detail, dropped by the exclude pattern. +#define MYLIB_DETAIL_INTERNAL 0 + +/// Public assert, kept. +#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) + +/// Another internal helper, dropped. +#define MYLIB_DETAIL_HELPER(y) ((y) + 1) diff --git a/test-files/golden-tests/config/exclude-symbols/macros-excluded.html b/test-files/golden-tests/config/exclude-symbols/macros-excluded.html new file mode 100644 index 0000000000..494b4686bb --- /dev/null +++ b/test-files/golden-tests/config/exclude-symbols/macros-excluded.html @@ -0,0 +1,57 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+MYLIB_ASSERT

+
+

Public assert, kept.

+
+
+
+

+Synopsis

+
+Declared in <macros-excluded.cpp>
+
#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort())
+
+
+
+
+

+MYLIB_VERSION

+
+

Public macro, kept.

+
+
+
+

+Synopsis

+
+Declared in <macros-excluded.cpp>
+
#define MYLIB_VERSION 1
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/exclude-symbols/macros-excluded.xml b/test-files/golden-tests/config/exclude-symbols/macros-excluded.xml new file mode 100644 index 0000000000..7b8a04e1a9 --- /dev/null +++ b/test-files/golden-tests/config/exclude-symbols/macros-excluded.xml @@ -0,0 +1,65 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + MYLIB_ASSERT + + + macros-excluded.cpp + macros-excluded.cpp + 10 + 9 + 1 + + + macro + zpmk/fWSZb4PC2i+Cn5p5Tu+EPc= + regular + + + brief + + text + Public assert, kept. + + + + 1 + x + #define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) + + + MYLIB_VERSION + + + macros-excluded.cpp + macros-excluded.cpp + 4 + 9 + 1 + + + macro + TUuQl/SYaUHhGiaN0r4LU6LxmuM= + regular + + + brief + + text + Public macro, kept. + + + + #define MYLIB_VERSION 1 + + diff --git a/test-files/golden-tests/config/exclude-symbols/macros-excluded.yml b/test-files/golden-tests/config/exclude-symbols/macros-excluded.yml new file mode 100644 index 0000000000..d7604b1773 --- /dev/null +++ b/test-files/golden-tests/config/exclude-symbols/macros-excluded.yml @@ -0,0 +1,2 @@ +exclude-symbols: + - 'MYLIB_DETAIL_*' diff --git a/test-files/golden-tests/config/extract-all/no-extract-all-macros.adoc b/test-files/golden-tests/config/extract-all/no-extract-all-macros.adoc new file mode 100644 index 0000000000..47ad9a72f6 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all-macros.adoc @@ -0,0 +1,50 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#DOC_FUNC] +== DOC_FUNC + +Documented function‐like macro. + +=== Synopsis + +Declared in `<no‐extract‐all‐macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define DOC_FUNC(x) (x) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| Argument. +|=== + +[#DOC_OBJECT] +== DOC_OBJECT + +Documented object‐like macro. + +=== Synopsis + +Declared in `<no‐extract‐all‐macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define DOC_OBJECT 2 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/extract-all/no-extract-all-macros.cpp b/test-files/golden-tests/config/extract-all/no-extract-all-macros.cpp new file mode 100644 index 0000000000..5e12c97451 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all-macros.cpp @@ -0,0 +1,14 @@ +// Undocumented macros are dropped when `extract-all` is off. + +#define UNDOC_OBJECT 1 + +/// Documented object-like macro. +#define DOC_OBJECT 2 + +#define UNDOC_FUNC(x) (x) + +/** Documented function-like macro. + + @param x Argument. +*/ +#define DOC_FUNC(x) (x) diff --git a/test-files/golden-tests/config/extract-all/no-extract-all-macros.html b/test-files/golden-tests/config/extract-all/no-extract-all-macros.html new file mode 100644 index 0000000000..3fe777fa00 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all-macros.html @@ -0,0 +1,75 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+DOC_FUNC

+
+

Documented function-like macro.

+
+
+
+

+Synopsis

+
+Declared in <no-extract-all-macros.cpp>
+
#define DOC_FUNC(x) (x)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
xArgument.
+
+
+
+
+

+DOC_OBJECT

+
+

Documented object-like macro.

+
+
+
+

+Synopsis

+
+Declared in <no-extract-all-macros.cpp>
+
#define DOC_OBJECT 2
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/extract-all/no-extract-all-macros.xml b/test-files/golden-tests/config/extract-all/no-extract-all-macros.xml new file mode 100644 index 0000000000..7646465780 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all-macros.xml @@ -0,0 +1,73 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + DOC_FUNC + + + no-extract-all-macros.cpp + no-extract-all-macros.cpp + 14 + 9 + 1 + + + macro + YOVxetLwt53qvrEAASZI8AzgYcs= + regular + + + brief + + text + Documented function-like macro. + + + + param + + text + Argument. + + x + + + 1 + x + #define DOC_FUNC(x) (x) + + + DOC_OBJECT + + + no-extract-all-macros.cpp + no-extract-all-macros.cpp + 6 + 9 + 1 + + + macro + M5AezpUJQJFafeSCRQUeS3DKV7Y= + regular + + + brief + + text + Documented object-like macro. + + + + #define DOC_OBJECT 2 + + diff --git a/test-files/golden-tests/config/extract-all/no-extract-all-macros.yml b/test-files/golden-tests/config/extract-all/no-extract-all-macros.yml new file mode 100644 index 0000000000..50005f5805 --- /dev/null +++ b/test-files/golden-tests/config/extract-all/no-extract-all-macros.yml @@ -0,0 +1,2 @@ +extract-all: false +warn-if-undocumented: false diff --git a/test-files/golden-tests/config/include-symbols/macros-allowlist.adoc b/test-files/golden-tests/config/include-symbols/macros-allowlist.adoc new file mode 100644 index 0000000000..8b09fae2ce --- /dev/null +++ b/test-files/golden-tests/config/include-symbols/macros-allowlist.adoc @@ -0,0 +1,54 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#MYLIB_ASSERT] +== MYLIB_ASSERT + +Public assert. + +=== Synopsis + +Declared in `<macros‐allowlist.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) +---- + +[#MYLIB_DETAIL_INTERNAL] +== MYLIB_DETAIL_INTERNAL + +Implementation detail (still under MYLIB_, so still allowed). + +=== Synopsis + +Declared in `<macros‐allowlist.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_DETAIL_INTERNAL 0 +---- + +[#MYLIB_VERSION] +== MYLIB_VERSION + +Public API version. + +=== Synopsis + +Declared in `<macros‐allowlist.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_VERSION 1 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/include-symbols/macros-allowlist.cpp b/test-files/golden-tests/config/include-symbols/macros-allowlist.cpp new file mode 100644 index 0000000000..03548ddc50 --- /dev/null +++ b/test-files/golden-tests/config/include-symbols/macros-allowlist.cpp @@ -0,0 +1,13 @@ +// Only macros matching the allowlist pattern are extracted. + +/// Public API version. +#define MYLIB_VERSION 1 + +/// Public assert. +#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) + +/// Implementation detail (still under MYLIB_, so still allowed). +#define MYLIB_DETAIL_INTERNAL 0 + +/// Unrelated macro outside the allowlist. +#define UNRELATED 42 diff --git a/test-files/golden-tests/config/include-symbols/macros-allowlist.html b/test-files/golden-tests/config/include-symbols/macros-allowlist.html new file mode 100644 index 0000000000..4c11f7c0f8 --- /dev/null +++ b/test-files/golden-tests/config/include-symbols/macros-allowlist.html @@ -0,0 +1,73 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+MYLIB_ASSERT

+
+

Public assert.

+
+
+
+

+Synopsis

+
+Declared in <macros-allowlist.cpp>
+
#define MYLIB_ASSERT(x) ((x) ? (void)0 : abort())
+
+
+
+
+

+MYLIB_DETAIL_INTERNAL

+
+

Implementation detail (still under MYLIB_, so still allowed).

+
+
+
+

+Synopsis

+
+Declared in <macros-allowlist.cpp>
+
#define MYLIB_DETAIL_INTERNAL 0
+
+
+
+
+

+MYLIB_VERSION

+
+

Public API version.

+
+
+
+

+Synopsis

+
+Declared in <macros-allowlist.cpp>
+
#define MYLIB_VERSION 1
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/include-symbols/macros-allowlist.xml b/test-files/golden-tests/config/include-symbols/macros-allowlist.xml new file mode 100644 index 0000000000..3aec68b396 --- /dev/null +++ b/test-files/golden-tests/config/include-symbols/macros-allowlist.xml @@ -0,0 +1,90 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + MYLIB_ASSERT + + + macros-allowlist.cpp + macros-allowlist.cpp + 7 + 9 + 1 + + + macro + 5aYnIreoz2nmY3g+d3dw/7uiZQo= + regular + + + brief + + text + Public assert. + + + + 1 + x + #define MYLIB_ASSERT(x) ((x) ? (void)0 : abort()) + + + MYLIB_DETAIL_INTERNAL + + + macros-allowlist.cpp + macros-allowlist.cpp + 10 + 9 + 1 + + + macro + jrhVPgeweqMSXJhxk1L0C+cC5Fg= + regular + + + brief + + text + Implementation detail (still under MYLIB_, so still allowed). + + + + #define MYLIB_DETAIL_INTERNAL 0 + + + MYLIB_VERSION + + + macros-allowlist.cpp + macros-allowlist.cpp + 4 + 9 + 1 + + + macro + NLotTY+r73fh/ANu0PneedVeqoo= + regular + + + brief + + text + Public API version. + + + + #define MYLIB_VERSION 1 + + diff --git a/test-files/golden-tests/config/include-symbols/macros-allowlist.yml b/test-files/golden-tests/config/include-symbols/macros-allowlist.yml new file mode 100644 index 0000000000..89bc108ed1 --- /dev/null +++ b/test-files/golden-tests/config/include-symbols/macros-allowlist.yml @@ -0,0 +1,2 @@ +include-symbols: + - 'MYLIB_*' diff --git a/test-files/golden-tests/config/multipage/multipage-macros.cpp b/test-files/golden-tests/config/multipage/multipage-macros.cpp new file mode 100644 index 0000000000..4b4be6861f --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.cpp @@ -0,0 +1,23 @@ +// Multipage rendering with corpus-level macros. +// +// Verifies that: +// - A `macros.{ext}` index page is generated. +// - The global-namespace page carries a "See also: Macros" +// navigation hint. +// - Each macro still gets its own per-symbol page. + +namespace foo { + +/// A function in a namespace. +void bar(); + +} // namespace foo + +/// Object-like macro. +#define MY_VERSION 1 + +/** Function-like macro. + + @param x The argument. +*/ +#define MY_INC(x) ((x) + 1) diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_INC.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_INC.adoc new file mode 100644 index 0000000000..b0c6f2ea3f --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_INC.adoc @@ -0,0 +1,27 @@ +[#MY_INC] += MY_INC +:mrdocs: + +Function‐like macro. + +== Synopsis + +Declared in `<multipage‐macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MY_INC(x) ((x) + 1) +---- + +== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| The argument. +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_VERSION.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_VERSION.adoc new file mode 100644 index 0000000000..6e32854acd --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/MY_VERSION.adoc @@ -0,0 +1,17 @@ +[#MY_VERSION] += MY_VERSION +:mrdocs: + +Object‐like macro. + +== Synopsis + +Declared in `<multipage‐macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MY_VERSION 1 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo.adoc new file mode 100644 index 0000000000..9576be415c --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo.adoc @@ -0,0 +1,15 @@ +[#foo] += foo +:mrdocs: + +== Functions + +[cols="1,4"] +|=== +| Name| Description +| xref:foo/bar.adoc[`bar`] +| A function in a namespace. +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo/bar.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo/bar.adoc new file mode 100644 index 0000000000..bbba553354 --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/foo/bar.adoc @@ -0,0 +1,19 @@ +[#foo-bar] += xref:foo.adoc[foo]::bar +:relfileprefix: ../ +:mrdocs: + +A function in a namespace. + +== Synopsis + +Declared in `<multipage‐macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +void +bar(); +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/index.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/index.adoc new file mode 100644 index 0000000000..44d1fbf5df --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/index.adoc @@ -0,0 +1,27 @@ +[#index] += Global namespace +:mrdocs: + +== Namespaces + +[cols=1] +|=== +| Name +| xref:foo.adoc[`foo`] +|=== + + +See also: <> +== xref:foo.adoc[foo] namespace + +=== Functions + +[cols="1,4"] +|=== +| Name| Description +| xref:foo/bar.adoc[`bar`] +| A function in a namespace. +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/macros.adoc b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/macros.adoc new file mode 100644 index 0000000000..4fd0c6ef5d --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/adoc/macros.adoc @@ -0,0 +1,14 @@ +[#macros] += Macros +:mrdocs: + +[cols="1,4"] +|=== +| Name| Description +| xref:MY_INC.adoc[`MY_INC`] +| Function‐like macro. +| xref:MY_VERSION.adoc[`MY_VERSION`] +| Object‐like macro. +|=== + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_INC.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_INC.html new file mode 100644 index 0000000000..3370d5d90f --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_INC.html @@ -0,0 +1,43 @@ + + +Reference: MY_INC + + + +
+

MY_INC

+
+

Function-like macro.

+
+
+

+Synopsis

+
+Declared in <multipage-macros.cpp>
+
#define MY_INC(x) ((x) + 1)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
xThe argument.
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_VERSION.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_VERSION.html new file mode 100644 index 0000000000..16af1096c3 --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/MY_VERSION.html @@ -0,0 +1,25 @@ + + +Reference: MY_VERSION + + + +
+

MY_VERSION

+
+

Object-like macro.

+
+
+

+Synopsis

+
+Declared in <multipage-macros.cpp>
+
#define MY_VERSION 1
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo.html new file mode 100644 index 0000000000..52f76454fb --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo.html @@ -0,0 +1,29 @@ + + +Reference: foo + + + +
+

foo

+

+Functions

+ + + + + + + + + + +
NameDescription
bar A function in a namespace.
+ + +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo/bar.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo/bar.html new file mode 100644 index 0000000000..870a77f155 --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/foo/bar.html @@ -0,0 +1,26 @@ + + +Reference: bar + + + +
+

foo::bar

+
+

A function in a namespace.

+
+
+

+Synopsis

+
+Declared in <multipage-macros.cpp>
+
void
+bar();
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/index.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/index.html new file mode 100644 index 0000000000..4872d32f7b --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/index.html @@ -0,0 +1,46 @@ + + +Reference + + + +
+

Global namespace

+

+Namespaces

+ + + + + + + + + + +
Name
foo
+ +

See also: Macros

+

+foo namespace

+

+Functions

+ + + + + + + + + + +
NameDescription
bar A function in a namespace.
+ + +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/macros.html b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/macros.html new file mode 100644 index 0000000000..70a133ecec --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/html/macros.html @@ -0,0 +1,27 @@ + + +Reference: Macros + + + +
+

Macros

+ + + + + + + + + + + +
NameDescription
MY_INC Function-like macro.
MY_VERSION Object-like macro.
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/config/multipage/multipage-macros.multipage/xml/reference.xml b/test-files/golden-tests/config/multipage/multipage-macros.multipage/xml/reference.xml new file mode 100644 index 0000000000..f6236d9ff3 --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.multipage/xml/reference.xml @@ -0,0 +1,124 @@ + + + + + + namespace + //////////////////////////8= + regular + + ZLLGQF+w25YYs6d8DiMh1c62qKg= + + + + foo + + + multipage-macros.cpp + multipage-macros.cpp + 9 + 1 + + + namespace + ZLLGQF+w25YYs6d8DiMh1c62qKg= + regular + //////////////////////////8= + + VlsdyNmsK+1/Bh/AFYtAk4YjeDo= + + + + bar + + + multipage-macros.cpp + multipage-macros.cpp + 12 + 1 + 1 + + + function + VlsdyNmsK+1/Bh/AFYtAk4YjeDo= + regular + ZLLGQF+w25YYs6d8DiMh1c62qKg= + + + brief + + text + A function in a namespace. + + + + + + identifier + void + + + normal + + + MY_INC + + + multipage-macros.cpp + multipage-macros.cpp + 23 + 9 + 1 + + + macro + e3lcif8pqE0lseQ8mHcQfPWPP/8= + regular + + + brief + + text + Function-like macro. + + + + param + + text + The argument. + + x + + + 1 + x + #define MY_INC(x) ((x) + 1) + + + MY_VERSION + + + multipage-macros.cpp + multipage-macros.cpp + 17 + 9 + 1 + + + macro + lF5HmKWzEdeN8YJFA9RemZ+0Ceo= + regular + + + brief + + text + Object-like macro. + + + + #define MY_VERSION 1 + + diff --git a/test-files/golden-tests/config/multipage/multipage-macros.yml b/test-files/golden-tests/config/multipage/multipage-macros.yml new file mode 100644 index 0000000000..51c4dcb0f9 --- /dev/null +++ b/test-files/golden-tests/config/multipage/multipage-macros.yml @@ -0,0 +1 @@ +multipage: true diff --git a/test-files/golden-tests/symbols/macro/macros-edge-cases.adoc b/test-files/golden-tests/symbols/macro/macros-edge-cases.adoc new file mode 100644 index 0000000000..e8d06c020b --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-edge-cases.adoc @@ -0,0 +1,74 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#DOC_THEN_BLANK] +== DOC_THEN_BLANK + +Documentation for `DOC_THEN_BLANK`; one blank line separates this comment from the macro definition. + +=== Synopsis + +Declared in `<macros‐edge‐cases.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define DOC_THEN_BLANK 1 +---- + +[#FIRST_THING_IN_FILE] +== FIRST_THING_IN_FILE + +=== Synopsis + +Declared in `<macros‐edge‐cases.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define FIRST_THING_IN_FILE 1 +---- + +[#REDEFINED_MACRO-07] +== REDEFINED_MACRO + +=== Synopsis + +Declared in `<macros‐edge‐cases.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define REDEFINED_MACRO 100 +---- + +[#REDEFINED_MACRO-06] +== REDEFINED_MACRO + +=== Synopsis + +Declared in `<macros‐edge‐cases.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define REDEFINED_MACRO 200 +---- + +[#SAME_LINE] +== SAME_LINE + +=== Synopsis + +Declared in `<macros‐edge‐cases.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +/** Doc‐comment that ends on the macro's line. */ #define SAME_LINE 2 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/symbols/macro/macros-edge-cases.cpp b/test-files/golden-tests/symbols/macro/macros-edge-cases.cpp new file mode 100644 index 0000000000..34d5dd8ef4 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-edge-cases.cpp @@ -0,0 +1,28 @@ +#define FIRST_THING_IN_FILE 1 + +// Edge cases for macro extraction. +// +// - `FIRST_THING_IN_FILE`'s definition starts with the +// first token in the file: there is no preceding comment +// of any kind. +// +// - `REDEFINED_MACRO` is defined twice (with an `#undef` +// in between) so the corpus ends up with two macro +// symbols that share a name. +// +// - `DOC_THEN_BLANK` is preceded by a doc comment with +// one blank line in between. +// +// - `SAME_LINE` has its doc comment on the same line as +// the directive. + +#define REDEFINED_MACRO 100 +#undef REDEFINED_MACRO +#define REDEFINED_MACRO 200 + +/// Documentation for `DOC_THEN_BLANK`; one blank line +/// separates this comment from the macro definition. + +#define DOC_THEN_BLANK 1 + +/** Doc-comment that ends on the macro's line. */ #define SAME_LINE 2 diff --git a/test-files/golden-tests/symbols/macro/macros-edge-cases.html b/test-files/golden-tests/symbols/macro/macros-edge-cases.html new file mode 100644 index 0000000000..7f4be1a34a --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-edge-cases.html @@ -0,0 +1,93 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+DOC_THEN_BLANK

+
+

Documentation for DOC_THEN_BLANK; one blank line separates this comment from the macro definition.

+
+
+
+

+Synopsis

+
+Declared in <macros-edge-cases.cpp>
+
#define DOC_THEN_BLANK 1
+
+
+
+
+

+FIRST_THING_IN_FILE

+
+
+

+Synopsis

+
+Declared in <macros-edge-cases.cpp>
+
#define FIRST_THING_IN_FILE 1
+
+
+
+
+

+REDEFINED_MACRO

+
+
+

+Synopsis

+
+Declared in <macros-edge-cases.cpp>
+
#define REDEFINED_MACRO 100
+
+
+
+
+

+REDEFINED_MACRO

+
+
+

+Synopsis

+
+Declared in <macros-edge-cases.cpp>
+
#define REDEFINED_MACRO 200
+
+
+
+
+

+SAME_LINE

+
+
+

+Synopsis

+
+Declared in <macros-edge-cases.cpp>
+
/** Doc-comment that ends on the macro's line. */ #define SAME_LINE 2
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/symbols/macro/macros-edge-cases.xml b/test-files/golden-tests/symbols/macro/macros-edge-cases.xml new file mode 100644 index 0000000000..06add66d3f --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-edge-cases.xml @@ -0,0 +1,109 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + DOC_THEN_BLANK + + + macros-edge-cases.cpp + macros-edge-cases.cpp + 26 + 9 + 1 + + + macro + VksLCOsoX6kp9d6q4HuFzBLb8xo= + regular + + + brief + + text + Documentation for + + + code + + text + DOC_THEN_BLANK + + + + text + ; one blank line separates this comment from the macro definition. + + + + #define DOC_THEN_BLANK 1 + + + FIRST_THING_IN_FILE + + + macros-edge-cases.cpp + macros-edge-cases.cpp + 1 + 9 + + + macro + P9XTbcFbElQn1aVzJgZB5Nz7ta4= + regular + #define FIRST_THING_IN_FILE 1 + + + REDEFINED_MACRO + + + macros-edge-cases.cpp + macros-edge-cases.cpp + 19 + 9 + + + macro + eLtMDTpwO9f7BuN0IHQocRcA67w= + regular + #define REDEFINED_MACRO 100 + + + REDEFINED_MACRO + + + macros-edge-cases.cpp + macros-edge-cases.cpp + 21 + 9 + + + macro + ZquBNb9/stV+itxeRAyg3lp1EZg= + regular + #define REDEFINED_MACRO 200 + + + SAME_LINE + + + macros-edge-cases.cpp + macros-edge-cases.cpp + 28 + 59 + + + macro + wX1s5MU5nHIMYAOXl99PcdwsALI= + regular + /** Doc-comment that ends on the macro's line. */ #define SAME_LINE 2 + + diff --git a/test-files/golden-tests/symbols/macro/macros-embedded.adoc b/test-files/golden-tests/symbols/macro/macros-embedded.adoc new file mode 100644 index 0000000000..12dff71c35 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-embedded.adoc @@ -0,0 +1,45 @@ +[#index] +== Global namespace + + +[#macros] +== Macros + +[#EMBED_FUNC] +== EMBED_FUNC + +A simple function‐like macro. + +=== Synopsis + +Declared in `<macros‐embedded.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define EMBED_FUNC(x) (x) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| The argument. +|=== + +[#EMBED_OBJECT] +== EMBED_OBJECT + +A simple object‐like macro. + +=== Synopsis + +Declared in `<macros‐embedded.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define EMBED_OBJECT 1 +---- + diff --git a/test-files/golden-tests/symbols/macro/macros-embedded.cpp b/test-files/golden-tests/symbols/macro/macros-embedded.cpp new file mode 100644 index 0000000000..a3e2978829 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-embedded.cpp @@ -0,0 +1,14 @@ +// Single-page output in embedded mode with at least one +// macro present. Exercises the embedded-mode branch of +// `HandlebarsGenerator::buildOne`, which is otherwise +// unreached by the wrapped-mode default of the rest of the +// suite. + +/// A simple object-like macro. +#define EMBED_OBJECT 1 + +/** A simple function-like macro. + + @param x The argument. +*/ +#define EMBED_FUNC(x) (x) diff --git a/test-files/golden-tests/symbols/macro/macros-embedded.html b/test-files/golden-tests/symbols/macro/macros-embedded.html new file mode 100644 index 0000000000..b43afe664a --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-embedded.html @@ -0,0 +1,60 @@ +
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+EMBED_FUNC

+
+

A simple function-like macro.

+
+
+
+

+Synopsis

+
+Declared in <macros-embedded.cpp>
+
#define EMBED_FUNC(x) (x)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
xThe argument.
+
+
+
+
+

+EMBED_OBJECT

+
+

A simple object-like macro.

+
+
+
+

+Synopsis

+
+Declared in <macros-embedded.cpp>
+
#define EMBED_OBJECT 1
+
+
diff --git a/test-files/golden-tests/symbols/macro/macros-embedded.xml b/test-files/golden-tests/symbols/macro/macros-embedded.xml new file mode 100644 index 0000000000..62858d3cfd --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-embedded.xml @@ -0,0 +1,73 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + EMBED_FUNC + + + macros-embedded.cpp + macros-embedded.cpp + 14 + 9 + 1 + + + macro + dKQ13hjXO28Z3yo8FyCCjm68rQE= + regular + + + brief + + text + A simple function-like macro. + + + + param + + text + The argument. + + x + + + 1 + x + #define EMBED_FUNC(x) (x) + + + EMBED_OBJECT + + + macros-embedded.cpp + macros-embedded.cpp + 8 + 9 + 1 + + + macro + QmBnp9AG+V093eLBvB2U+oCsDLA= + regular + + + brief + + text + A simple object-like macro. + + + + #define EMBED_OBJECT 1 + + diff --git a/test-files/golden-tests/symbols/macro/macros-embedded.yml b/test-files/golden-tests/symbols/macro/macros-embedded.yml new file mode 100644 index 0000000000..603e9b1172 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-embedded.yml @@ -0,0 +1 @@ +embedded: true diff --git a/test-files/golden-tests/symbols/macro/macros-extraction-modes.adoc b/test-files/golden-tests/symbols/macro/macros-extraction-modes.adoc new file mode 100644 index 0000000000..015a7ef63a --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-extraction-modes.adoc @@ -0,0 +1,60 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#MYLIB_REGULAR] +== MYLIB_REGULAR + +A regular macro (not matched by any filter). + +=== Synopsis + +Declared in `<macros‐extraction‐modes.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_REGULAR 1 +---- + +[#MYLIB_SEE_BELOW] +== MYLIB_SEE_BELOW + +A real‐world example of macro using "see below". + +=== Synopsis + +Declared in `<macros‐extraction‐modes.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MYLIB_SEE_BELOW(cond, msg) /* see-below */ +---- + +=== Description + +Assert that `cond` holds; otherwise terminate with a diagnostic that includes `msg.` + +Evaluates `cond.` If the result is non‐zero, the macro has no observable effect. Otherwise, it reports a diagnostic that includes `msg` and the source location of the failing call, then terminates the program. + +The diagnostic format and the termination mechanism are implementation details that may change between releases. Clients should rely only on the behavior described here. + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *cond* +| Any expression contextually convertible to `bool`. +| *msg* +| A null‐terminated C string describing the assertion. +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/symbols/macro/macros-extraction-modes.cpp b/test-files/golden-tests/symbols/macro/macros-extraction-modes.cpp new file mode 100644 index 0000000000..605a9699e7 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-extraction-modes.cpp @@ -0,0 +1,34 @@ +// Exercises the `implementation-defined` and `see-below` +// filters. The existing `macros-excluded.cpp` and +// `macros-allowlist.cpp` cover only exclude / include. + +/// A regular macro (not matched by any filter). +#define MYLIB_REGULAR 1 + +/// Macro matched by `implementation-defined`. The synopsis +/// should be elided in the rendered output. +#define MYLIB_IMPL_DETAIL 0 + +/** A real-world example of macro using "see below". + + Assert that @p cond holds; otherwise terminate with a + diagnostic that includes @p msg. + + Evaluates @p cond. If the result is non-zero, the macro + has no observable effect. Otherwise, it reports a + diagnostic that includes @p msg and the source location + of the failing call, then terminates the program. + + The diagnostic format and the termination mechanism are + implementation details that may change between releases. + Clients should rely only on the behavior described here. + + @param cond Any expression contextually convertible + to `bool`. + @param msg A null-terminated C string describing + the assertion. +*/ +#define MYLIB_SEE_BELOW(cond, msg) \ + ((cond) ? (void)0 \ + : ::mylib_detail::assert_fail( \ + msg, __FILE__, __LINE__)) diff --git a/test-files/golden-tests/symbols/macro/macros-extraction-modes.html b/test-files/golden-tests/symbols/macro/macros-extraction-modes.html new file mode 100644 index 0000000000..1da57b8200 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-extraction-modes.html @@ -0,0 +1,86 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+MYLIB_REGULAR

+
+

A regular macro (not matched by any filter).

+
+
+
+

+Synopsis

+
+Declared in <macros-extraction-modes.cpp>
+
#define MYLIB_REGULAR 1
+
+
+
+
+

+MYLIB_SEE_BELOW

+
+

A real-world example of macro using "see below".

+
+
+
+

+Synopsis

+
+Declared in <macros-extraction-modes.cpp>
+
#define MYLIB_SEE_BELOW(cond, msg) /* see-below */
+
+
+

+Description

+

Assert that cond holds; otherwise terminate with a diagnostic that includes msg.

+

Evaluates cond. If the result is non-zero, the macro has no observable effect. Otherwise, it reports a diagnostic that includes msg and the source location of the failing call, then terminates the program.

+

The diagnostic format and the termination mechanism are implementation details that may change between releases. Clients should rely only on the behavior described here.

+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
condAny expression contextually convertible to bool.
msgA null-terminated C string describing the assertion.
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/symbols/macro/macros-extraction-modes.xml b/test-files/golden-tests/symbols/macro/macros-extraction-modes.xml new file mode 100644 index 0000000000..66fe8302a1 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-extraction-modes.xml @@ -0,0 +1,193 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + MYLIB_IMPL_DETAIL + + + macros-extraction-modes.cpp + macros-extraction-modes.cpp + 10 + 9 + 1 + + + macro + g4wKUYuiXd+IwGcAhtpa4kR8fnw= + implementation-defined + + + brief + + text + Macro matched by + + + code + + text + implementation-defined + + + + text + . The synopsis should be elided in the rendered output. + + + + #define MYLIB_IMPL_DETAIL 0 + + + MYLIB_REGULAR + + + macros-extraction-modes.cpp + macros-extraction-modes.cpp + 6 + 9 + 1 + + + macro + 23KZ2eWDLEDe5G7eAYRjzeRJUGs= + regular + + + brief + + text + A regular macro (not matched by any filter). + + + + #define MYLIB_REGULAR 1 + + + MYLIB_SEE_BELOW + + + macros-extraction-modes.cpp + macros-extraction-modes.cpp + 31 + 9 + 1 + + + macro + eejfITdsZ2A7UPvV7SybSxidOMg= + see-below + + + paragraph + + text + Assert that + + + code + + text + cond + + + + text + holds; otherwise terminate with a diagnostic that includes + + + code + + text + msg. + + + + + paragraph + + text + Evaluates + + + code + + text + cond. + + + + text + If the result is non-zero, the macro has no observable effect. Otherwise, it reports a diagnostic that includes + + + code + + text + msg + + + + text + and the source location of the failing call, then terminates the program. + + + + paragraph + + text + The diagnostic format and the termination mechanism are implementation details that may change between releases. Clients should rely only on the behavior described here. + + + + brief + + text + A real-world example of macro using "see below". + + + + param + + text + Any expression contextually convertible to + + + code + + text + bool + + + + text + . + + cond + + + param + + text + A null-terminated C string describing the assertion. + + msg + + + 1 + cond + msg + #define MYLIB_SEE_BELOW(cond, msg) \ + ((cond) ? (void)0 \ + : ::mylib_detail::assert_fail( \ + msg, __FILE__, __LINE__)) + + diff --git a/test-files/golden-tests/symbols/macro/macros-extraction-modes.yml b/test-files/golden-tests/symbols/macro/macros-extraction-modes.yml new file mode 100644 index 0000000000..fbc91c0196 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-extraction-modes.yml @@ -0,0 +1,4 @@ +implementation-defined: + - 'MYLIB_IMPL_*' +see-below: + - 'MYLIB_SEE_*' diff --git a/test-files/golden-tests/symbols/macro/macros-multi-file.adoc b/test-files/golden-tests/symbols/macro/macros-multi-file.adoc new file mode 100644 index 0000000000..f940cebf9d --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-multi-file.adoc @@ -0,0 +1,36 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#SHARED_MACRO-08] +== SHARED_MACRO + +=== Synopsis + +Declared in `<macros‐multi‐file.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define SHARED_MACRO 2 +---- + +[#SHARED_MACRO-01] +== SHARED_MACRO + +=== Synopsis + +Declared in `<macros‐multi‐file.hpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define SHARED_MACRO 1 +---- + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/symbols/macro/macros-multi-file.cpp b/test-files/golden-tests/symbols/macro/macros-multi-file.cpp new file mode 100644 index 0000000000..1739b1ce43 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-multi-file.cpp @@ -0,0 +1,10 @@ +// Multi-file macro extraction. The companion header +// `macros-multi-file.hpp` defines `SHARED_MACRO`; this +// translation unit `#undef`s it and redefines it. The +// resulting corpus has two macro symbols sharing the +// `SHARED_MACRO` name but living in different files. + +#include "macros-multi-file.hpp" + +#undef SHARED_MACRO +#define SHARED_MACRO 2 diff --git a/test-files/golden-tests/symbols/macro/macros-multi-file.hpp b/test-files/golden-tests/symbols/macro/macros-multi-file.hpp new file mode 100644 index 0000000000..8fa388f2a9 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-multi-file.hpp @@ -0,0 +1,5 @@ +// Companion header for macros-multi-file.cpp. The macro +// defined here shares its name with one defined in the +// .cpp, so the corpus ends up with two macro symbols whose +// only difference is the source file. +#define SHARED_MACRO 1 diff --git a/test-files/golden-tests/symbols/macro/macros-multi-file.html b/test-files/golden-tests/symbols/macro/macros-multi-file.html new file mode 100644 index 0000000000..192e6c648c --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-multi-file.html @@ -0,0 +1,51 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+SHARED_MACRO

+
+
+

+Synopsis

+
+Declared in <macros-multi-file.cpp>
+
#define SHARED_MACRO 2
+
+
+
+
+

+SHARED_MACRO

+
+
+

+Synopsis

+
+Declared in <macros-multi-file.hpp>
+
#define SHARED_MACRO 1
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/symbols/macro/macros-multi-file.xml b/test-files/golden-tests/symbols/macro/macros-multi-file.xml new file mode 100644 index 0000000000..11ef102a14 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros-multi-file.xml @@ -0,0 +1,43 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + SHARED_MACRO + + + macros-multi-file.cpp + macros-multi-file.cpp + 10 + 9 + + + macro + imZCXdfBh7L6pkYUJAzqGAqPFaM= + regular + #define SHARED_MACRO 2 + + + SHARED_MACRO + + + macros-multi-file.hpp + macros-multi-file.hpp + 5 + 9 + + + macro + HMkriio6NHkJxBGLQMnNE/EqPGc= + regular + #define SHARED_MACRO 1 + + diff --git a/test-files/golden-tests/symbols/macro/macros.adoc b/test-files/golden-tests/symbols/macro/macros.adoc new file mode 100644 index 0000000000..52e85a1803 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros.adoc @@ -0,0 +1,397 @@ += Reference +:mrdocs: + +[#index] +== Global namespace + + +[#macros] +== Macros + +[#ADD] +== ADD + +Add two values. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define ADD(a, b) ((a) + (b)) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *a* +| First operand. +| *b* +| Second operand. +|=== + +[#ANSWER] +== ANSWER + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define ANSWER \ + (40 \ + + \ + 2) +---- + +[#DUP] +== DUP + +Duplicate parameter documentation. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define DUP(x) (x) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| One thing. +| *x* +| Same thing again. +|=== + +[#EMPTY] +== EMPTY + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define EMPTY +---- + +[#FOO] +== FOO + +A documentation comment. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define FOO 1 +---- + +[#GREETING] +== GREETING + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define GREETING "hello" +---- + +[#LOG] +== LOG + +Log `msg` to `stderr`, tagged with the current file and line number. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define LOG(msg) fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *msg* +| The message to log. +|=== + +[#LOG_F] +== LOG_F + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define LOG_F(fmt, ...) printf(fmt, __VA_ARGS__) +---- + +[#MEDIAN3] +== MEDIAN3 + +Compute the median of three values. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MEDIAN3(a, b, c) \ + (((a) > (b)) ? \ + (((b) > (c)) ? (b) : (((a) > (c)) ? (c) : (a))) : \ + (((a) > (c)) ? (a) : (((b) > (c)) ? (c) : (b)))) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *a,b,c* +| The three values to compare. +|=== + +[#MIN] +== MIN + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MIN(a, b) \ + ((a) < (b) \ + ? (a) \ + : (b)) +---- + +[#MISALIGNED] +== MISALIGNED + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define MISALIGNED(x) ( \ +(x) ) +---- + +[#NON_VARIADIC] +== NON_VARIADIC + +Non‐variadic macro that mistakenly documents a variadic argument list. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define NON_VARIADIC(x) (x) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| The argument. +| *...* +| Not really variadic. +|=== + +[#OBJECT_WITH_PARAM] +== OBJECT_WITH_PARAM + +Object‐like macro with a spurious parameter documentation block. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define OBJECT_WITH_PARAM 1 +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *x* +| Should not be here. +|=== + +[#PI] +== PI + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define PI 3.14 +---- + +[#PRINTF] +== PRINTF + +Variadic `printf` wrapper, documenting the variadic argument list with the conventional `...` form. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *fmt* +| The format string. +| *...* +| Format arguments. +|=== + +[#SQUARE] +== SQUARE + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define SQUARE(x) ( \ + (x) \ + * \ + (x)) +---- + +[#SUM] +== SUM + +Sum a variadic list of values. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define SUM(...) (__VA_ARGS__) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *...* +| The values to sum. +|=== + +[#VLOG] +== VLOG + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define VLOG(...) printf(__VA_ARGS__) +---- + +[#VPRINTF] +== VPRINTF + +Variadic printf wrapper, documented with the explicit `*VA_ARGS*` name. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define VPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *fmt* +| The format string. +| *__VA_ARGS__* +| Format arguments. +|=== + +[#WRONG_PARAM] +== WRONG_PARAM + +Documentation that names a parameter the macro does not actually have. + +=== Synopsis + +Declared in `<macros.cpp>` + +[source,cpp,subs="verbatim,replacements,macros,-callouts"] +---- +#define WRONG_PARAM(x) (x) +---- + +=== Parameters + +[cols="1,4"] +|=== +|Name|Description + +| *y* +| Not actually a parameter of WRONG_PARAM. +|=== + + +[.small]#Created with https://www.mrdocs.com[MrDocs]# diff --git a/test-files/golden-tests/symbols/macro/macros.cpp b/test-files/golden-tests/symbols/macro/macros.cpp new file mode 100644 index 0000000000..fe5c311275 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros.cpp @@ -0,0 +1,118 @@ +// Object-like macros with various body shapes. +/// A documentation comment. +#define FOO 1 +#define PI 3.14 +#define GREETING "hello" + +// Object-like macro that expands to nothing. +#define EMPTY + +/** Add two values. + + @param a First operand. + @param b Second operand. +*/ +#define ADD(a, b) ((a) + (b)) + +/** Log @p msg to `stderr`, tagged with the current file and line number. + + @param msg The message to log. +*/ +#define LOG(msg) fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg) + +// Variadic macro. +#define LOG_F(fmt, ...) printf(fmt, __VA_ARGS__) + +// Variadic-only (no named parameters). +#define VLOG(...) printf(__VA_ARGS__) + +// Body spread across lines via backslash continuation. +#define ANSWER \ + (40 \ + + \ + 2) + +// Function-like macro with line continuation. +#define MIN(a, b) \ + ((a) < (b) \ + ? (a) \ + : (b)) + +// Macro definition with whitespace between `#` and `define`, plus +// backslashes. The backslashes should remain aligned in the output, +// despite the removal of the whitespace between `#` and `define`. +# define SQUARE(x) ( \ + (x) \ + * \ + (x)) + +// Body with a continuation line having fewer leading +// whitespace chars than were stripped from the `#define` +// line. +# define MISALIGNED(x) ( \ + (x) ) + +/** Sum a variadic list of values. + + @param ... The values to sum. +*/ +#define SUM(...) (__VA_ARGS__) + +/** Variadic `printf` wrapper, documenting the variadic + argument list with the conventional `...` form. + + @param fmt The format string. + @param ... Format arguments. +*/ +#define PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) + +/** Variadic printf wrapper, documented with the explicit + `__VA_ARGS__` name. + + @param fmt The format string. + @param __VA_ARGS__ Format arguments. +*/ +#define VPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) + +/** Compute the median of three values. + + @param a,b,c The three values to compare. +*/ +#define MEDIAN3(a, b, c) \ + (((a) > (b)) ? \ + (((b) > (c)) ? (b) : (((a) > (c)) ? (c) : (a))) : \ + (((a) > (c)) ? (a) : (((b) > (c)) ? (c) : (b)))) + +// --- Intentionally bad doc comments. These exercise the +// parameter-doc validation warnings that fire during +// finalization. The macros still render normally; the +// warnings go to `stderr` only. --- + +/** Duplicate parameter documentation. + + @param x One thing. + @param x Same thing again. +*/ +#define DUP(x) (x) + +/** Documentation that names a parameter the macro + does not actually have. + + @param y Not actually a parameter of WRONG_PARAM. +*/ +#define WRONG_PARAM(x) (x) + +/** Non-variadic macro that mistakenly documents a + variadic argument list. + + @param x The argument. + @param ... Not really variadic. +*/ +#define NON_VARIADIC(x) (x) + +/** Object-like macro with a spurious parameter + documentation block. + + @param x Should not be here. +*/ +#define OBJECT_WITH_PARAM 1 diff --git a/test-files/golden-tests/symbols/macro/macros.html b/test-files/golden-tests/symbols/macro/macros.html new file mode 100644 index 0000000000..34de8ed35f --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros.html @@ -0,0 +1,531 @@ + + +Reference + + + +
+

Reference

+
+
+

+Global Namespace

+
+
+
+

+Macros

+
+
+
+

+ADD

+
+

Add two values.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define ADD(a, b) ((a) + (b))
+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
aFirst operand.
bSecond operand.
+
+
+
+
+

+ANSWER

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define ANSWER  \
+    (40         \
+    +           \
+    2)
+
+
+
+
+

+DUP

+
+

Duplicate parameter documentation.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define DUP(x) (x)
+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
xOne thing.
xSame thing again.
+
+
+
+
+

+EMPTY

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define EMPTY
+
+
+
+
+

+FOO

+
+

A documentation comment.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define FOO 1
+
+
+
+
+

+GREETING

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define GREETING "hello"
+
+
+
+
+

+LOG

+
+

Log msg to stderr, tagged with the current file and line number.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define LOG(msg) fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
msgThe message to log.
+
+
+
+
+

+LOG_F

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define LOG_F(fmt, ...) printf(fmt, __VA_ARGS__)
+
+
+
+
+

+MEDIAN3

+
+

Compute the median of three values.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define MEDIAN3(a, b, c) \
+    (((a) > (b)) ? \
+        (((b) > (c)) ? (b) : (((a) > (c)) ? (c) : (a))) : \
+        (((a) > (c)) ? (a) : (((b) > (c)) ? (c) : (b))))
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
a,b,cThe three values to compare.
+
+
+
+
+

+MIN

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define MIN(a, b)        \
+    ((a) < (b)           \
+        ? (a)            \
+        : (b))
+
+
+
+
+

+MISALIGNED

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define MISALIGNED(x) ( \
+(x) )
+
+
+
+
+

+NON_VARIADIC

+
+

Non-variadic macro that mistakenly documents a variadic argument list.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define NON_VARIADIC(x) (x)
+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
xThe argument.
...Not really variadic.
+
+
+
+
+

+OBJECT_WITH_PARAM

+
+

Object-like macro with a spurious parameter documentation block.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define OBJECT_WITH_PARAM 1
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
xShould not be here.
+
+
+
+
+

+PI

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define PI 3.14
+
+
+
+
+

+PRINTF

+
+

Variadic printf wrapper, documenting the variadic argument list with the conventional ... form.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
fmtThe format string.
...Format arguments.
+
+
+
+
+

+SQUARE

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define SQUARE(x) (  \
+ (x)                 \
+ *                   \
+ (x))
+
+
+
+
+

+SUM

+
+

Sum a variadic list of values.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define SUM(...) (__VA_ARGS__)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
...The values to sum.
+
+
+
+
+

+VLOG

+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define VLOG(...) printf(__VA_ARGS__)
+
+
+
+
+

+VPRINTF

+
+

Variadic printf wrapper, documented with the explicit VA_ARGS name.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define VPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+
+
+

+Parameters

+ + + + + + + + + + + + + + + + + +
NameDescription
fmtThe format string.
__VA_ARGS__Format arguments.
+
+
+
+
+

+WRONG_PARAM

+
+

Documentation that names a parameter the macro does not actually have.

+
+
+
+

+Synopsis

+
+Declared in <macros.cpp>
+
#define WRONG_PARAM(x) (x)
+
+
+

+Parameters

+ + + + + + + + + + + + + +
NameDescription
yNot actually a parameter of WRONG_PARAM.
+
+
+ +
+ + + \ No newline at end of file diff --git a/test-files/golden-tests/symbols/macro/macros.xml b/test-files/golden-tests/symbols/macro/macros.xml new file mode 100644 index 0000000000..12fe073478 --- /dev/null +++ b/test-files/golden-tests/symbols/macro/macros.xml @@ -0,0 +1,649 @@ + + + + + + namespace + //////////////////////////8= + regular + + + + + ADD + + + macros.cpp + macros.cpp + 15 + 9 + 1 + + + macro + MP7mYzEdeappgTJzAemMe5FqOU4= + regular + + + brief + + text + Add two values. + + + + param + + text + First operand. + + a + + + param + + text + Second operand. + + b + + + 1 + a + b + #define ADD(a, b) ((a) + (b)) + + + ANSWER + + + macros.cpp + macros.cpp + 30 + 9 + + + macro + f0/wMt2xJcts9h5dMRQ1E+MRwCY= + regular + #define ANSWER \ + (40 \ + + \ + 2) + + + DUP + + + macros.cpp + macros.cpp + 96 + 9 + 1 + + + macro + /tZIhzbj1kaoV/ald6lrsWG2K7Y= + regular + + + brief + + text + Duplicate parameter documentation. + + + + param + + text + One thing. + + x + + + param + + text + Same thing again. + + x + + + 1 + x + #define DUP(x) (x) + + + EMPTY + + + macros.cpp + macros.cpp + 8 + 9 + + + macro + KoifUNf2t7SNFeSLsCeQ1RyNebY= + regular + #define EMPTY + + + FOO + + + macros.cpp + macros.cpp + 3 + 9 + 1 + + + macro + ptTbT94Bt66wbojBYoy4u+7o1Hc= + regular + + + brief + + text + A documentation comment. + + + + #define FOO 1 + + + GREETING + + + macros.cpp + macros.cpp + 5 + 9 + + + macro + tafGZZMhSOCeQ/dDgVlmoncEgaE= + regular + #define GREETING "hello" + + + LOG + + + macros.cpp + macros.cpp + 21 + 9 + 1 + + + macro + 8/A2cY0kE7sZGPu8Fm5yvKqTfDk= + regular + + + brief + + text + Log + + + code + + text + msg + + + + text + to + + + code + + text + stderr + + + + text + , tagged with the current file and line number. + + + + param + + text + The message to log. + + msg + + + 1 + msg + #define LOG(msg) fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg) + + + LOG_F + + + macros.cpp + macros.cpp + 24 + 9 + + + macro + 6Mn4XjyslUFDQ556TeALbn1FZLQ= + regular + 1 + fmt + 1 + #define LOG_F(fmt, ...) printf(fmt, __VA_ARGS__) + + + MEDIAN3 + + + macros.cpp + macros.cpp + 81 + 9 + 1 + + + macro + xfbyTDvHx0QSYyZRYQkG3qqW0E0= + regular + + + brief + + text + Compute the median of three values. + + + + param + + text + The three values to compare. + + a,b,c + + + 1 + a + b + c + #define MEDIAN3(a, b, c) \ + (((a) > (b)) ? \ + (((b) > (c)) ? (b) : (((a) > (c)) ? (c) : (a))) : \ + (((a) > (c)) ? (a) : (((b) > (c)) ? (c) : (b)))) + + + MIN + + + macros.cpp + macros.cpp + 36 + 9 + + + macro + wAOX+g0Gpie0rutt5TkkfSLrSyA= + regular + 1 + a + b + #define MIN(a, b) \ + ((a) < (b) \ + ? (a) \ + : (b)) + + + MISALIGNED + + + macros.cpp + macros.cpp + 52 + 12 + + + macro + WKu1NehbFa7yTFgob8Ds6IEANvI= + regular + 1 + x + #define MISALIGNED(x) ( \ +(x) ) + + + NON_VARIADIC + + + macros.cpp + macros.cpp + 111 + 9 + 1 + + + macro + ZF+tzqQBq/C2LM8RmesqCE2/lzA= + regular + + + brief + + text + Non-variadic macro that mistakenly documents a variadic argument list. + + + + param + + text + The argument. + + x + + + param + + text + Not really variadic. + + ... + + + 1 + x + #define NON_VARIADIC(x) (x) + + + OBJECT_WITH_PARAM + + + macros.cpp + macros.cpp + 118 + 9 + 1 + + + macro + OMZCXFVxuqrYVSboYPj5R0q1AE8= + regular + + + brief + + text + Object-like macro with a spurious parameter documentation block. + + + + param + + text + Should not be here. + + x + + + #define OBJECT_WITH_PARAM 1 + + + PI + + + macros.cpp + macros.cpp + 4 + 9 + + + macro + M4Pd0ObGWFJOaFoyBYE7LWDRsDo= + regular + #define PI 3.14 + + + PRINTF + + + macros.cpp + macros.cpp + 67 + 9 + 1 + + + macro + bxhWKi0DV8ENh9E51psV9ncyx8U= + regular + + + brief + + text + Variadic + + + code + + text + printf + + + + text + wrapper, documenting the variadic argument list with the conventional + + + code + + text + ... + + + + text + form. + + + + param + + text + The format string. + + fmt + + + param + + text + Format arguments. + + ... + + + 1 + fmt + 1 + #define PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) + + + SQUARE + + + macros.cpp + macros.cpp + 44 + 12 + + + macro + 0KkyI59evAWoQa5CB5/S5Vf0osE= + regular + 1 + x + #define SQUARE(x) ( \ + (x) \ + * \ + (x)) + + + SUM + + + macros.cpp + macros.cpp + 59 + 9 + 1 + + + macro + r7SpPd3s44538df2kcz5PmgU3qw= + regular + + + brief + + text + Sum a variadic list of values. + + + + param + + text + The values to sum. + + ... + + + 1 + 1 + #define SUM(...) (__VA_ARGS__) + + + VLOG + + + macros.cpp + macros.cpp + 27 + 9 + + + macro + 6Wct9mQFvyn61ijoJH64B5TQKbQ= + regular + 1 + 1 + #define VLOG(...) printf(__VA_ARGS__) + + + VPRINTF + + + macros.cpp + macros.cpp + 75 + 9 + 1 + + + macro + vhhFJ33LNlb2les8kFGapwWaNgo= + regular + + + brief + + text + Variadic printf wrapper, documented with the explicit + + + code + + strong + + text + VA_ARGS + + + + + text + name. + + + + param + + text + The format string. + + fmt + + + param + + text + Format arguments. + + __VA_ARGS__ + + + 1 + fmt + 1 + #define VPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) + + + WRONG_PARAM + + + macros.cpp + macros.cpp + 103 + 9 + 1 + + + macro + V2xtJPqO7jo1Lyh52NiPXfKrkzQ= + regular + + + brief + + text + Documentation that names a parameter the macro does not actually have. + + + + param + + text + Not actually a parameter of WRONG_PARAM. + + y + + + 1 + x + #define WRONG_PARAM(x) (x) + +