microsoft/doxygen-comments
>- Conventions and a checklist for writing, updating, and correcting Doxygen documentation comments in WIL's public C++ headers under `include/` so they match the repository's style and build without warnings. Applies only to headers under `include/` — not `tests/` or other code. Use when adding or fixing `//!` or `/** */` doc comments, `@param`/`@tparam`/`@return` tags, `@ref`/`@see` cross-references, or briefs, or when resolving warnings from the Doxygen `docs` build target.
npx skills add https://github.com/microsoft/wil --skill doxygen-comments
WIL's public API is documented with Doxygen comments in the headers under include/wil/, and the docs are generated from
docs/Doxyfile. Use this skill to add, update, or fix those comments so they stay consistent with the codebase and generate
cleanly.
This skill applies only to WIL's public headers under include/ (primarily include/wil/*.h). Do not apply these
conventions to test code (tests/), documentation sources (docs/), packaging, or build scripts — those files are not part of
the generated API reference. If a request targets files outside include/, this skill does not apply.
Do not rewrite comments purely for style, and do not document private/internal helpers (see "Public API only" below).
//! line comments or /** ... */ block comments. Match the stylealready used in the surrounding file / nearby declarations instead of mixing both. //! is common for file banners and member
comments; /** */ is common for free functions and templates.
@, never \. Use @param, @tparam, @return, @brief, @ingroup, @ref, @see, @note, @file,etc. The codebase uses zero backslash-style tags — keep it that way.
JAVADOC_AUTOBRIEF makes the first sentence (up to the first period) the brief.Keep it on its own physical line; start the detailed description on the next line. An explicit @brief is rarely needed.
.clang-format (ColumnLimit: 130) and applies to comment text too.EXTRACT_ALL = NO and EXTRACT_PRIVATE = NO, so private members and undocumented internals are notemitted. Focus documentation on the public surface; don't add Doxygen tags to private helpers expecting them to appear in the
output.
details namespaces. WIL's implementation details live in namespaces named details (and similarly namedvariants such as details_abi). These must not emit documentation — wrap the entire namespace in a /// @cond … /// @endcond
pair (note the /// marker used for these structural tags) so Doxygen skips its contents. Put /// @cond on its own line
immediately before the namespace and /// @endcond immediately after its closing brace. See the example below.
~~~ fenced example for functions whose correctuse isn't clear from the signature alone — e.g. callback or functor contracts (what the callback must do and return), paired or
multi-step call sequences, RAII helpers whose placement or lifetime matters, round-trip or reverse operations, or subtle
buffer/ownership conventions. Skip examples for self-explanatory helpers such as simple getters, predicates, or arithmetic.
Doxygen fenced code blocks are delimited with ~~~ (any matching run of three or more tildes); inside //! banners, prefix
each example line with //!.
@ref <name> and @see, and group related members with @ingroup <group>(for example @ingroup outparam).
wil::; STL-mirroring pieces live under wistd::.PREDEFINED macro over a per-guard escape; use WIL_DOXYGEN only when needed. Doxygen evaluates #if guardsagainst docs/Doxyfile's PREDEFINED list, which already forces many conditions true in docs — e.g.
WINAPI_FAMILY_PARTITION(partition)=1, WIL_USE_STL=1, WIL_ENABLE_EXCEPTIONS, WIL_RESOURCE_STL, and many __cpp_lib_*
feature macros. First check whether the guard is already satisfied there; if so, no escape is needed. If a declaration is
gated on a feature macro that is broadly useful, add that macro to PREDEFINED rather than sprinkling escapes. Only when the
condition can't be satisfied that way — notably the mutually-exclusive __WIL_* / __WIL_*_STL header-wrapper guards in
resource.h/registry.h — OR || defined(WIL_DOXYGEN) into the condition (convert #ifdef X / #ifndef X to
#if defined(X); when the line wraps, continue with a trailing \ and put defined(WIL_DOXYGEN) on the next line). A
standalone #ifdef WIL_DOXYGEN block is for doc-only constructs with no real declaration to attach to.
WI_NOEXCEPT → noexcept,WI_NODISCARD → [[nodiscard]], and others in docs/Doxyfile's PREDEFINED), so document the logical signature rather than
the macro-heavy source.
When adding or fixing comments, verify:
@param/@tparam name matches the signature — no stale, missing, misspelled, or reordered names. This is the mostcommon source of WARN_IF_DOC_ERROR warnings.
@return is present and accurate for functions that return a meaningful value; omit it for void._failfast, and_nothrow/NoThrow variants — state how the function reports failure (throws, fail-fasts, or returns an HRESULT).
@ (convert any \param, \brief, etc.).@ref/@see targets exist and are spelled correctly.10. Internal details/details_* namespaces are wrapped in /// @cond … /// @endcond so their contents are excluded from
the generated documentation.
11. Conditionally-compiled public declarations are visible in docs — the guard is either already satisfied by PREDEFINED
or ORs in || defined(WIL_DOXYGEN) (reserved for conditions PREDEFINED can't cover, like the header-wrapper guards).
12. Non-obvious functions carry a ~~~ usage example — anything with a callback contract, a paired/multi-step call sequence,
or subtle ownership/buffer semantics shows how to call it; trivial helpers do not.
Doxygen is configured with WARN_IF_UNDOCUMENTED = YES and WARN_IF_DOC_ERROR = YES, and formats warnings as `$file:$line:
$text, so a docs build points directly at problems. From an already-configured build directory, run the docs` target (it
requires Doxygen on PATH, and WIL_BUILD_TESTS = ON, which is the default):
ninja docs # run from the configured build directory, e.g. build/msvc
The docs target just runs Doxygen, so it is independent of the selected configuration. Review its output for new warnings
referencing the files you touched — mismatched parameters, undocumented public members, and broken references all surface there.
Generated HTML lands under the build directory. If Doxygen is not installed, at minimum re-check each comment against the current
signature using the checklist above.
Correcting stale parameter names and backslash tags:
// Before — backslash tags, @param name doesn't match the signature, missing @return.
/** Opens the widget.
\param widgetName The name to open.
*/
HRESULT open_widget_nothrow(PCWSTR name, wil::unique_hwidget& widget);
// After — @-style tags, names match the signature, failure mode and return documented, wrapped at 130 columns.
/** Opens the named widget.
Returns an error code rather than throwing, so it is safe for callers built without exceptions.
@param name Name of the widget to open.
@param widget Receives the opened widget on success.
@return `S_OK` on success, or a failure `HRESULT` from the underlying `OpenWidget` call. */
HRESULT open_widget_nothrow(PCWSTR name, wil::unique_hwidget& widget);
Hiding an internal details namespace so Doxygen excludes it from the output:
/// @cond
namespace details
{
// Internal implementation — intentionally undocumented.
template <typename T>
using ensure_trivially_destructible_t = typename ensure_trivially_destructible<T>::type;
} // namespace details
/// @endcond
Adding a WIL_DOXYGEN escape so a guarded public declaration still appears in the docs:
// Before — only compiled when <objbase.h> has been included, so Doxygen never sees this signature.
#if defined(__WIL_OBJBASE_H_)
template <typename T>
com_ptr<T> make_com_ptr(T* ptr);
#endif
// After — OR in defined(WIL_DOXYGEN) (which Doxygen predefines) so the signature is documented.
#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN)
template <typename T>
com_ptr<T> make_com_ptr(T* ptr);
#endif
This skill is an intentional starting point. The conventions above are grounded in the current headers and docs/Doxyfile, but
the checklist and examples are expected to grow as we refine it. When a rule here disagrees with real, nearby code in the file you
are editing, prefer matching the surrounding code and flag the discrepancy.
Take microsoft/doxygen-comments from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.