Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/modules/ROOT/pages/configuration/filters.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,84 @@ include::example$snippets/options/extract-private/extract-private.yml[tags=!test
include::example$snippets/options/extract-private/extract-private.adoc[tags=!footer]
========

== Macros

MrDocs also documents macros.

.Example
[source,cpp]
----
include::example$snippets/options/extract-all-macros/extract-all-macros.cpp[]
----

[.adoc-preview]
========
include::example$snippets/options/extract-all-macros/extract-all-macros.adoc[tags=!footer]
========

By default, only documented macros are included in the documentation. Here the public `MY_LIB_VERSION_STRING` is kept, while the intermediary `MY_LIB_DETAIL_STRINGIZE` helper, which carries an ordinary comment rather than a documentation comment, is dropped. This is because the xref:configuration/reference.adoc#extract-all-macros_option[`extract-all-macros`] option defaults to `false`. Set it to `true` to extract every macro that passes the filters, whether it is documented or not.

Macros follow different rules from other symbols, so they get their own name filters, which otherwise work like their symbol counterparts: xref:configuration/reference.adoc#include-macros_option[`include-macros`] and xref:configuration/reference.adoc#exclude-macros_option[`exclude-macros`]. `include-macros` narrows the extracted macros by name:

.Example
[source,cpp]
----
include::example$snippets/options/include-macros/include-macros.cpp[]
----

.`mrdocs.yml`
[source,yaml]
----
include::example$snippets/options/include-macros/include-macros.yml[tags=!test-only]
----

[.adoc-preview]
========
include::example$snippets/options/include-macros/include-macros.adoc[tags=!footer]
========

`MY_LIB_VERSION` is kept; `OTHER_HELPER` falls outside the `MY_LIB_*` allowlist.

=== Excluding macros

`exclude-macros` drops macros by name, which suits the helpers a library defines only as an implementation detail:

.Example
[source,cpp]
----
include::example$snippets/options/exclude-macros/exclude-macros.cpp[]
----

.`mrdocs.yml`
[source,yaml]
----
include::example$snippets/options/exclude-macros/exclude-macros.yml[tags=!test-only]
----

[.adoc-preview]
========
include::example$snippets/options/exclude-macros/exclude-macros.adoc[tags=!footer]
========

`MY_LIB_ASSERT` is kept; the `MY_LIB_DETAIL_*` and `+*_IMPL+` helpers are dropped.

NOTE: Macros have only include and exclude filters, with no xref:configuration/reference.adoc#implementation-defined_option[`implementation-defined`] or xref:configuration/reference.adoc#see-below_option[`see-below`] equivalents. Those modes keep a symbol in the corpus while hiding its scope or eliding its synopsis body, and neither fits a macro: a macro has no members to hide, and since the preprocessor replaces macros before the other symbols are extracted, a macro never appears in another symbol's synopsis. A macro is therefore only ever included or excluded.

=== Feature testing macros

A feature-test or configuration macro is often not defined in the build MrDocs sees, so the preprocessor never reports it. MrDocs always defines `+__MRDOCS__+`, so guard a definition with it to document the macro anyway:

.Example
[source,cpp]
----
include::example$snippets/options/macros-guard/macros-guard.cpp[]
----

[.adoc-preview]
========
include::example$snippets/options/macros-guard/macros-guard.adoc[tags=!footer]
========

== Glob syntax

Symbol and file patterns share the same wildcards (the file form uses `/` as the path delimiter; the symbol form uses `::`):
Expand Down
28 changes: 28 additions & 0 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@
"title": "Input directories to exclude",
"type": "array"
},
"exclude-macros": {
"default": [],
"description": "A macro whose name matches one of these patterns, or one of `exclude-symbols`, is not extracted even if it would otherwise be included. See `include-symbols` for the pattern syntax.",
"items": {
"type": "string"
},
"title": "Macro patterns to exclude",
"type": "array"
},
"exclude-patterns": {
"default": [],
"description": "Files matching these glob patterns are skipped, even when they sit inside an included directory. The patterns are relative to the configuration file. A single `*` matches everything in a directory; `**` matches everything in a directory and its subdirectories.",
Expand Down Expand Up @@ -163,6 +172,16 @@
"title": "Extract all symbols",
"type": "boolean"
},
"extract-all-macros": {
"default": false,
"description": "Like `extract-all`, but for preprocessor macros. When `false` (the default), MrDocs extracts only macros that carry a documentation comment, on the assumption that an undocumented macro is an implementation detail while a documented one is public API meant to be shown. When `true`, every macro that passes the macro filters is extracted whether documented or not. This is independent of `extract-all`, which does not apply to macros.",
"enum": [
true,
false
],
"title": "Extract all macros",
"type": "boolean"
},
"extract-anonymous-namespaces": {
"default": true,
"description": "Determine whether symbols in anonymous namespaces should be extracted.",
Expand Down Expand Up @@ -339,6 +358,15 @@
"title": "Symbols rendered as \"implementation-defined\"",
"type": "array"
},
"include-macros": {
"default": [],
"description": "If any patterns are defined here, only macros whose name matches one of them (or one of `include-symbols`) are extracted. When empty (the default), every macro is a candidate, so with `extract-all-macros` off the documented macros are shown and can then be narrowed with these patterns. Macro names are unqualified, so namespace-scoped `include-symbols` patterns never match a macro; use these patterns to scope macros by name (e.g. `MY_LIB_*`). A macro also listed in `include-symbols` is still included. See `include-symbols` for the pattern syntax.",
"items": {
"type": "string"
},
"title": "Macro patterns to include",
"type": "array"
},
"include-symbols": {
"default": [],
"description": "If any patterns are defined here, only symbols that match one of these patterns are extracted. The patterns are applied to the fully qualified name of the symbol without any leading \"::\". A single \"*\" will match all symbols in the namespace. Double \"**\" will match all symbols in the namespace and its subnamespaces. The patterns also support \"?\" for any chars, \"[<chars>]\" for charsets, \"[^<chars>]\" for inverted charsets, and \"{<glob>,...}\" for alternatives.",
Expand Down
10 changes: 10 additions & 0 deletions docs/mrdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ file-patterns:
- '*.hpp'
include-symbols:
- 'mrdocs::**'
# MrDocs's own macros are maintainer-facing implementation helpers, not
# public API: the MRDOCS_* support macros and the internal X-macros
# (INFO/LOG/F) and compat shims used across the headers. Keep them out of
# MrDocs's own reference.
exclude-macros:
- 'MRDOCS_*'
- 'INFO'
- 'LOG'
- 'F'
- 'FMT_CONSTEVAL'
implementation-defined:
- '**::detail'
# Injected by the MRDOCS_DESCRIBE_* macros
Expand Down
1 change: 1 addition & 0 deletions include/mrdocs/Metadata.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <mrdocs/Metadata/Symbol/Friend.hpp>
#include <mrdocs/Metadata/Symbol/Function.hpp>
#include <mrdocs/Metadata/Symbol/Guide.hpp>
#include <mrdocs/Metadata/Symbol/Macro.hpp>
#include <mrdocs/Metadata/Symbol/Namespace.hpp>
#include <mrdocs/Metadata/Symbol/NamespaceAlias.hpp>
#include <mrdocs/Metadata/Symbol/Overloads.hpp>
Expand Down
74 changes: 74 additions & 0 deletions include/mrdocs/Metadata/Symbol/Macro.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// 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)
// Copyright (c) 2026 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_API_METADATA_SYMBOL_MACRO_HPP
#define MRDOCS_API_METADATA_SYMBOL_MACRO_HPP

#include <mrdocs/Metadata/Symbol.hpp>
#include <mrdocs/Support/Describe.hpp>
#include <string>
#include <vector>

namespace mrdocs {

/** Info for preprocessor macros.

Covers both object-like and function-like macros.
*/
struct MacroSymbol final
: SymbolCommonBase<SymbolKind::Macro>
{
/** Whether this is a function-like macro.

This is not derivable from @ref Parameters and
@ref IsVariadic: a function-like macro can have an
empty parameter list. `#define F` is object-like,
while `#define F()` is function-like with no
parameters, and only this flag tells them apart.
*/
bool IsFunctionLike = false;

/** The names of the macro's parameters.

Empty for object-like macros and for function-like
macros with no parameters. For variadic
function-like macros, this lists only the
named parameters; variadicness is indicated
by @ref IsVariadic.
*/
std::vector<std::string> Parameters;

/** Whether the macro takes a variadic argument list.

True for macros declared with `...`, such as
`#define LOG(fmt, ...) ...`.
*/
bool IsVariadic = false;

//--------------------------------------------

/** Create a macro symbol bound to an ID.
*/
explicit MacroSymbol(SymbolID const& ID) noexcept
: SymbolCommonBase(ID)
{
}
};

MRDOCS_DESCRIBE_STRUCT(
MacroSymbol,
(SymbolCommonBase<SymbolKind::Macro>),
(IsFunctionLike, Parameters, IsVariadic)
)

} // mrdocs

#endif // MRDOCS_API_METADATA_SYMBOL_MACRO_HPP
40 changes: 20 additions & 20 deletions include/mrdocs/Metadata/Symbol/Namespace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2024 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//
Expand Down Expand Up @@ -55,13 +56,21 @@ struct NamespaceTranche {
*/
std::vector<SymbolID> Usings;

/** Preprocessor macros.

Only the global namespace's tranche carries these:
macros have no C++ scope, so they are collected at the
top level rather than under any named namespace.
*/
std::vector<SymbolID> Macros;

};

MRDOCS_DESCRIBE_STRUCT(
NamespaceTranche,
(),
(Namespaces, NamespaceAliases, Typedefs, Records, Enums,
Functions, Variables, Concepts, Guides, Usings)
Functions, Variables, Concepts, Guides, Usings, Macros)
)

/** Join all tranche member lists into a single view.
Expand All @@ -71,25 +80,16 @@ inline
auto
allMembers(NamespaceTranche const& T)
{
// This is a trick to emulate views::concat in C++20
return std::views::transform(
std::views::iota(0, 10),
[&T](int const i) -> auto const& {
switch (i) {
case 0: return T.Namespaces;
case 1: return T.NamespaceAliases;
case 2: return T.Typedefs;
case 3: return T.Records;
case 4: return T.Enums;
case 5: return T.Functions;
case 6: return T.Variables;
case 7: return T.Concepts;
case 8: return T.Guides;
case 9: return T.Usings;
default: throw std::out_of_range("Invalid index");
}
}
) | std::ranges::views::join;
// Concatenate every member list (emulating C++26 views::concat).
// The lists are discovered by reflection, so adding a member to
// NamespaceTranche extends this automatically, no switch to keep
// in sync.
static constexpr auto members =
describe::memberPointers<NamespaceTranche>();
return members
| std::views::transform(
[&T](auto const p) -> auto const& { return T.*p; })
| std::ranges::views::join;
}

/** Describes a namespace and its members.
Expand Down
23 changes: 10 additions & 13 deletions include/mrdocs/Metadata/Symbol/RecordInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//
Expand Down Expand Up @@ -75,19 +76,15 @@ inline
auto
allMembers(RecordInterface const& T)
{
// This is a trick to emulate views::concat in C++20
return
std::views::iota(0, 3) |
std::views::transform(
[&T](int i) -> auto {
switch (i) {
case 0: return allMembers(T.Public);
case 1: return allMembers(T.Protected);
case 2: return allMembers(T.Private);
default: throw std::out_of_range("Invalid index");
}
}) |
std::ranges::views::join;
// Concatenate the access tranches (emulating C++26 views::concat).
// The tranches are discovered by reflection, so this needs no
// hand-maintained per-tranche switch.
static constexpr auto tranches =
describe::memberPointers<RecordInterface>();
return tranches
| std::views::transform(
[&T](auto const p) { return allMembers(T.*p); })
| std::ranges::views::join;
}

} // mrdocs
Expand Down
32 changes: 11 additions & 21 deletions include/mrdocs/Metadata/Symbol/RecordTranche.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//
Expand Down Expand Up @@ -81,27 +82,16 @@ inline
auto
allMembers(RecordTranche const& T)
{
// This is a trick to emulate views::concat in C++20
return std::views::transform(
std::views::iota(0, 11),
[&T](int const i) -> auto const&
{
switch (i) {
case 0: return T.NamespaceAliases;
case 1: return T.Typedefs;
case 2: return T.Records;
case 3: return T.Enums;
case 4: return T.Functions;
case 5: return T.StaticFunctions;
case 6: return T.Variables;
case 7: return T.StaticVariables;
case 8: return T.Concepts;
case 9: return T.Guides;
case 10: return T.Usings;
default: throw std::out_of_range("Invalid index");
}
}
) | std::ranges::views::join;
// Concatenate every member list (emulating C++26 views::concat).
// The lists are discovered by reflection, so adding a member to
// RecordTranche extends this automatically, no switch to keep in
// sync.
static constexpr auto members =
describe::memberPointers<RecordTranche>();
return members
| std::views::transform(
[&T](auto const p) -> auto const& { return T.*p; })
| std::ranges::views::join;
}

} // mrdocs
Expand Down
1 change: 1 addition & 0 deletions include/mrdocs/Metadata/Symbol/SymbolNodes.inc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ INFO(Guide)
INFO(NamespaceAlias)
INFO(Using)
INFO(Concept)
INFO(Macro)

#undef INFO

Loading
Loading