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
90 changes: 58 additions & 32 deletions docs/modules/ROOT/pages/extensions/dom-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,64 @@ The `Symbol` object represents a symbol extracted from the source code. The symb
| `Any`
| The documentation for the symbol.

| `attributes`
| `<<attribute-fields,Attribute Object[]>>`
| The C++ attributes attached to the symbol, for example `[[deprecated]]` or `[[nodiscard]]`.

Comment thread
alandefreitas marked this conversation as resolved.
|===

[#attribute-fields]
=== Attribute

The `Attribute` object represents a single C++ attribute. Every attribute object has these common fields:

|===
|Property |Type| Description

| `kind`
| `string`
| The recognized standard attribute, identified independently of spelling: one of `deprecated`, `nodiscard`, `maybe-unused`, `no-unique-address`, `noreturn`, `carries-dependency`, `fallthrough`, `likely`, `unlikely`, `assume`, `indeterminate`, or `other` for an unrecognized attribute.

| `name`
| `string`
| The attribute name. Recognized attributes use the normalized spelling (e.g., `deprecated`, `no_unique_address`); unrecognized ones keep the written spelling, including any scope (e.g., `gnu::custom`).

| `balancedTokens`
| `string[]`
| The arguments as a balanced-token sequence, one per element (e.g., `["printf", "1", "2"]` for `[[gnu::format(printf, 1, 2)]]`). String-literal arguments keep their quotes. Empty for attributes that take no arguments.

|===

Some attribute kinds add a field for an evaluated argument. Templates should branch on `kind` and read these directly (for example, the deprecation notice prints `message`) rather than parsing `balancedTokens`.

When the attribute kind is `deprecated`, the attribute object has the following additional property:

|===
|Property |Type| Description

| `message`
| `string`
| The deprecation message, without quotes (e.g., `use bar instead` for `[[deprecated("use bar instead")]]`). Empty when no message was given.
|===

When the attribute kind is `nodiscard`, the attribute object has the following additional property:

|===
|Property |Type| Description

| `reason`
| `string`
| The reason the result should not be discarded, without quotes (the C++20 `[[nodiscard("reason")]]` form). Empty otherwise.
|===

When the attribute kind is `assume`, the attribute object has the following additional property:

|===
|Property |Type| Description

| `expression`
| `string`
| The assumed expression (the C++23 `[[assume(expression)]]` form).
|===

Handlebars generators extend each symbol with the following fields:
Expand Down Expand Up @@ -217,10 +275,6 @@ When the symbol kind is `function`, the symbol object has the following addition
| `bool`
| Whether the function is deleted as written.

| `isNoReturn`
| `bool`
| Whether the function is noreturn.

| `hasOverrideAttr`
| `bool`
| Whether the function has the override attribute.
Expand All @@ -241,10 +295,6 @@ When the symbol kind is `function`, the symbol object has the following addition
| `bool`
| Whether the function is final.

| `isNodiscard`
| `bool`
| Whether the function is nodiscard.

| `isExplicitObjectMemberFunction`
| `bool`
| Whether the function is an explicit object member function.
Expand Down Expand Up @@ -292,10 +342,6 @@ When the symbol kind is `function`, the symbol object has the following addition
| `requires`
| `string`
| The `requires` expression of the function.

| `attributes`
| `string[]`
| The attributes of the function.
|===

When the symbol kind is `typedef`, the symbol object has the following additional properties:
Expand Down Expand Up @@ -352,10 +398,6 @@ When the symbol kind is `variable`, the symbol object has the following addition
| `initializer`
| `string`
| The initializer of the variable.

| `attributes`
| `string[]`
| The attributes of the variable.
|===

When the symbol kind is `field` (i.e. non-static data members), the symbol object has the following additional properties:
Expand All @@ -371,14 +413,6 @@ When the symbol kind is `field` (i.e. non-static data members), the symbol objec
| `string`
| The default value of the field.

| `isMaybeUnused`
| `bool`
| Whether the field is maybe unused.

| `isDeprecated`
| `bool`
| Whether the field is deprecated.

| `isVariant`
| `bool`
| Whether the field is a variant.
Expand All @@ -391,17 +425,9 @@ When the symbol kind is `field` (i.e. non-static data members), the symbol objec
| `bool`
| Whether the field is a bitfield.

| `hasNoUniqueAddress`
| `string`
| Whether the field has the `[[no_unique_address]]` attribute.

| `bitfieldWidth`
| `string`
| The width of the bitfield.

| `attributes`
| `string[]`
| The attributes of the field.
|===

When the symbol kind is `friend`, the symbol object has the following additional properties:
Expand Down
41 changes: 41 additions & 0 deletions include/mrdocs/Metadata/Attribute/AssumeAttribute.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// 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 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_API_METADATA_ATTRIBUTE_ASSUMEATTRIBUTE_HPP
#define MRDOCS_API_METADATA_ATTRIBUTE_ASSUMEATTRIBUTE_HPP

#include <mrdocs/Platform.hpp>
#include <mrdocs/Metadata/Attribute/AttributeBase.hpp>
#include <mrdocs/Support/Describe.hpp>
#include <string>

namespace mrdocs {

/** The `[[assume(expression)]]` attribute (C++23).

States that the expression always evaluates to true at this point.
*/
struct AssumeAttribute final
: AttributeCommonBase<AttributeKind::Assume>
{
/** The assumed expression, as written.
*/
std::string Expression;
};

MRDOCS_DESCRIBE_STRUCT(
AssumeAttribute,
(Attribute),
(Expression)
)

} // mrdocs

#endif // MRDOCS_API_METADATA_ATTRIBUTE_ASSUMEATTRIBUTE_HPP
179 changes: 179 additions & 0 deletions include/mrdocs/Metadata/Attribute/AttributeBase.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//
// 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 Alan de Freitas (alandefreitas@gmail.com)
//
// Official repository: https://github.com/cppalliance/mrdocs
//

#ifndef MRDOCS_API_METADATA_ATTRIBUTE_ATTRIBUTEBASE_HPP
#define MRDOCS_API_METADATA_ATTRIBUTE_ATTRIBUTEBASE_HPP

#include <mrdocs/Platform.hpp>
#include <mrdocs/ADT/Polymorphic.hpp>
#include <mrdocs/Metadata/Attribute/AttributeKind.hpp>
#include <mrdocs/Support/CompareReflectedType.hpp>
#include <mrdocs/Support/Describe.hpp>
#include <string>
#include <vector>

namespace mrdocs {

class DomCorpus;

/* Forward declarations
*/
#define INFO(PascalName) struct PascalName##Attribute;
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

/** A C++ attribute attached to a symbol.

This base class stores the information common to every
attribute: the kind, the spelling name, and the raw
balanced-token sequence written between the parentheses.
Derived classes add the evaluated arguments for the
attributes that take them.
*/
struct Attribute {
/** The kind of attribute.
*/
AttributeKind Kind;

/** The attribute name as it appears in the standard form.

For recognized attributes this is the normalized spelling
(e.g. `deprecated`, `no_unique_address`). For unrecognized
attributes (the `Other` kind) it is the spelling as written,
including any scope (e.g. `gnu::custom`).
*/
std::string Name;

/** The attribute arguments as a balanced-token sequence.

Each element is one top-level argument as rendered by Clang,
e.g. `{"printf", "1", "2"}` for `[[gnu::format(printf, 1, 2)]]`.
Empty for attributes that take no arguments. This is the raw
token form; derived classes expose evaluated arguments.
*/
std::vector<std::string> balancedTokens;

/** View this instance as a const Attribute reference.
*/
constexpr Attribute const& asAttribute() const noexcept
{
return *this;
}

/** View this instance as a mutable Attribute reference.
*/
constexpr Attribute& asAttribute() noexcept
{
return *this;
}

#define INFO(PascalName) constexpr bool is##PascalName() const noexcept { \
return Kind == AttributeKind::PascalName; \
}
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

#define INFO(PascalName) \
constexpr PascalName##Attribute const& as##PascalName() const noexcept { \
if (Kind == AttributeKind::PascalName) \
return reinterpret_cast<PascalName##Attribute const&>(*this); \
MRDOCS_UNREACHABLE(); \
}
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

#define INFO(PascalName) \
constexpr PascalName##Attribute & as##PascalName() noexcept { \
if (Kind == AttributeKind::PascalName) \
return reinterpret_cast<PascalName##Attribute&>(*this); \
MRDOCS_UNREACHABLE(); \
}
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

#define INFO(PascalName) \
constexpr PascalName##Attribute const* as##PascalName##Ptr() const noexcept { \
if (Kind == AttributeKind::PascalName) { return reinterpret_cast<PascalName##Attribute const*>(this); } \
return nullptr; \
}
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

#define INFO(PascalName) \
constexpr PascalName##Attribute * as##PascalName##Ptr() noexcept { \
if (Kind == AttributeKind::PascalName) { return reinterpret_cast<PascalName##Attribute *>(this); } \
return nullptr; \
}
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

protected:
/** Virtual destructor for polymorphic base.
*/
constexpr virtual ~Attribute() = default;

/** Construct with a concrete attribute kind.
*/
constexpr explicit Attribute(AttributeKind kind) noexcept
: Kind(kind)
{
}
};

MRDOCS_DESCRIBE_STRUCT(
Attribute,
(),
(Kind, Name, balancedTokens)
)

/** Serialize an Attribute into a DOM value.
*/
MRDOCS_DECL
void
tag_invoke(
dom::ValueFromTag,
dom::Value& v,
Attribute const& I,
DomCorpus const* domCorpus);

/** CRTP base that ties a concrete attribute to a fixed AttributeKind.
*/
template<AttributeKind K>
struct AttributeCommonBase : Attribute {
/** Static discriminator for the concrete attribute.
*/
static constexpr AttributeKind kind_id = K;

#define INFO(PascalName) \
static constexpr bool is##PascalName() noexcept { return K == AttributeKind::PascalName; }
#include <mrdocs/Metadata/Attribute/AttributeNodes.inc>

MRDOCS_DESCRIBE_CLASS(AttributeCommonBase, (Attribute), ())

protected:
/** Construct the base with the fixed kind.
*/
constexpr AttributeCommonBase() noexcept
: Attribute(K)
{
}
};

/** Compare two polymorphic attributes by visitor dispatch.
*/
MRDOCS_DECL
std::strong_ordering
operator<=>(Polymorphic<Attribute> const&, Polymorphic<Attribute> const&);

/** Equality for two polymorphic attributes.
*/
inline bool
operator==(Polymorphic<Attribute> const& a, Polymorphic<Attribute> const& b)
{
return std::is_eq(a <=> b);
}

} // mrdocs

#endif // MRDOCS_API_METADATA_ATTRIBUTE_ATTRIBUTEBASE_HPP
Loading
Loading