From 459c02bc2e0b192bb5fbe3f87932a3fe45b9847f Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 20 Jul 2026 12:05:25 -0700 Subject: [PATCH 1/4] Docs for com.h --- include/wil/com.h | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/include/wil/com.h b/include/wil/com.h index 60bd6a41..53d9aa95 100644 --- a/include/wil/com.h +++ b/include/wil/com.h @@ -922,16 +922,16 @@ class com_ptr_t // Error-policy driven forms of com_ptr #ifdef WIL_ENABLE_EXCEPTIONS -//! COM pointer, errors throw exceptions (see @ref com_ptr_t for details) +//! COM pointer, errors throw exceptions (see @ref wil::com_ptr_t for details) template using com_ptr = com_ptr_t; #endif -//! COM pointer, errors return error codes (see @ref com_ptr_t for details) +//! COM pointer, errors return error codes (see @ref wil::com_ptr_t for details) template using com_ptr_nothrow = com_ptr_t; -//! COM pointer, errors fail-fast (see @ref com_ptr_t for details) +//! COM pointer, errors fail-fast (see @ref wil::com_ptr_t for details) template using com_ptr_failfast = com_ptr_t; @@ -1223,7 +1223,7 @@ inline bool operator<=(TLeft* left, const com_ptr_t& right) WI //! forwarding reference template that can be used as an input com pointer. That input com pointer is allowed to be any of: //! * Raw Pointer: `T* com_raw_ptr(T* ptr)` //! * Wil com_ptr: `T* com_raw_ptr(const wil::com_ptr_t& ptr)` -//! * WRL ComPtr: `T* com_raw_ptr(const ::Microsoft::WRL::ComPtr& ptr)` +//! * WRL ComPtr: `T* com_raw_ptr(const Microsoft::WRL::ComPtr& ptr)` //! * C++/CX hat: `IInspectable* com_raw_ptr(Platform::Object^ ptr)` //! //! Which in turn allows code like the following to be written: @@ -1794,13 +1794,15 @@ inline U^ cx_dynamic_cast(T&& ptrSource) WI_NOEXCEPT #if (NTDDI_VERSION >= NTDDI_WINBLUE) #ifdef WIL_ENABLE_EXCEPTIONS -//! Agile reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_agile_query for details) +//! Agile reference to a COM interface, errors throw exceptions (see @ref wil::com_ptr_t and @ref wil::com_agile_query for +//! details) using com_agile_ref = com_ptr; #endif -//! Agile reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_agile_query_nothrow for -//! details) +//! Agile reference to a COM interface, errors return error codes (see @ref wil::com_ptr_t and @ref wil::com_agile_query_nothrow +//! for details) using com_agile_ref_nothrow = com_ptr_nothrow; -//! Agile reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_agile_query_failfast for details) +//! Agile reference to a COM interface, errors fail fast (see @ref wil::com_ptr_t and @ref wil::com_agile_query_failfast for +//! details) using com_agile_ref_failfast = com_ptr_failfast; //! @name Create agile reference helpers @@ -1913,12 +1915,14 @@ namespace details } // namespace details #ifdef WIL_ENABLE_EXCEPTIONS -//! Weak reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_weak_query for details) +//! Weak reference to a COM interface, errors throw exceptions (see @ref wil::com_ptr_t and @ref wil::com_weak_query for details) using com_weak_ref = com_ptr; #endif -//! Weak reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_weak_query_nothrow for details) +//! Weak reference to a COM interface, errors return error codes (see @ref wil::com_ptr_t and @ref wil::com_weak_query_nothrow +//! for details) using com_weak_ref_nothrow = com_ptr_nothrow; -//! Weak reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_weak_query_failfast for details) +//! Weak reference to a COM interface, errors fail fast (see @ref wil::com_ptr_t and @ref wil::com_weak_query_failfast for +//! details) using com_weak_ref_failfast = com_ptr_failfast; //! @name Create weak reference helpers @@ -2008,6 +2012,10 @@ HRESULT com_weak_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IWeak #pragma region COM Object Helpers +//! Returns whether the given COM object is agile (that is, whether it implements `IAgileObject`). +//! @tparam T A raw interface pointer, any wil `com_ptr`, or a WRL `ComPtr`. +//! @param ptrSource The object to test. +//! @return `true` if the object implements `IAgileObject`, `false` otherwise. template inline bool is_agile(T&& ptrSource) { @@ -3058,7 +3066,9 @@ class stream_position_saver m_stream.reset(); } + //! Move constructor; transfers the saved stream position. stream_position_saver(stream_position_saver&&) = default; + //! Move assignment operator; transfers the saved stream position. stream_position_saver& operator=(stream_position_saver&&) = default; stream_position_saver(const stream_position_saver&) = delete; @@ -3082,6 +3092,8 @@ namespace details } // namespace details /// @endcond +//! Unique RAII type that clears an object's site by calling `IObjectWithSite::SetSite(nullptr)` on destruction, breaking +//! the site cycle. using unique_set_site_null_call = wil::unique_com_call; /** RAII support for managing the site chain. This function sets the site pointer on an object and return an object @@ -3378,6 +3390,9 @@ template class com_timeout_t { public: + //! Establishes the call timeout on the current thread for this object's lifetime. + //! @param timeoutInMilliseconds The per-call timeout, in milliseconds, after which blocked cross-apartment calls are + //! canceled. com_timeout_t(DWORD timeoutInMilliseconds) : m_threadId(GetCurrentThreadId()) { const HRESULT cancelEnablementResult = CoEnableCallCancellation(nullptr); @@ -3397,6 +3412,7 @@ class com_timeout_t } } + //! Returns whether the timeout was successfully established (the underlying threadpool timer was created). operator bool() const noexcept { // All construction calls must succeed to provide us with a non-null m_timer value. @@ -3433,14 +3449,14 @@ class com_timeout_t // Error-policy driven forms of com_timeout #ifdef WIL_ENABLE_EXCEPTIONS -//! COM timeout, errors throw exceptions (see @ref com_timeout_t for details) +//! COM timeout, errors throw exceptions (see @ref wil::com_timeout_t for details) using com_timeout = com_timeout_t; #endif -//! COM timeout, errors return error codes (see @ref com_timeout_t for details) +//! COM timeout, errors return error codes (see @ref wil::com_timeout_t for details) using com_timeout_nothrow = com_timeout_t; -//! COM timeout, errors fail-fast (see @ref com_timeout_t for details) +//! COM timeout, errors fail-fast (see @ref wil::com_timeout_t for details) using com_timeout_failfast = com_timeout_t; #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) From 35cbf8ee4bfff590c5d5c7a4b96f123199be5090 Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 20 Jul 2026 13:30:12 -0700 Subject: [PATCH 2/4] Skill update --- .github/skills/doxygen-comments/SKILL.md | 85 ++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/.github/skills/doxygen-comments/SKILL.md b/.github/skills/doxygen-comments/SKILL.md index 3b27ef9b..15cacca3 100644 --- a/.github/skills/doxygen-comments/SKILL.md +++ b/.github/skills/doxygen-comments/SKILL.md @@ -43,7 +43,15 @@ Do **not** rewrite comments purely for style, and do not document private/intern - **Hide internal `details` namespaces.** WIL's implementation details live in namespaces named `details` (and similarly named variants 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. + immediately before the namespace and `/// @endcond` immediately after its closing brace. See the example below. **Keep the + `@cond`/`@endcond` region brace-balanced:** every `{` opened inside must also close inside, and never let an *enclosing* + scope's closing brace (such as a `} // namespace wil` wrapping the `details` block) fall inside it. Hiding a scope's closing + brace makes Doxygen miss the close and corrupts parsing for the whole build — see the diagnosis notes below. +- **Hide implementation overloads and specializations, not just `details`.** When a public name is really an overload set, or a + primary template plus helper specializations, that all live directly in `wil::` (SFINAE overloads, or type specializations like + `verify_bool` / `verify_bool`), document the one primary that carries the contract and wrap the remaining + implementation overloads/specializations in `/// @cond` … `/// @endcond`. This keeps the reference page to a single clear entry + instead of a wall of near-identical mechanical ones. - **Add a usage example when the call pattern is non-obvious.** Include a short `~~~` fenced example for functions whose correct use 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 @@ -51,7 +59,14 @@ Do **not** rewrite comments purely for style, and do not document private/intern Doxygen fenced code blocks are delimited with `~~~` (any matching run of three or more tildes); inside `//!` banners, prefix each example line with `//!`. - **Cross-references.** Link to other entities with `@ref ` and `@see`, and group related members with `@ingroup ` - (for example `@ingroup outparam`). + (for example `@ingroup outparam`). Doxygen auto-links a documented name wherever it appears in text, so a bare `@ref ` + (or even the bare name) usually links fine when scopes are intact — including class and function templates such as `com_ptr_t` + or `com_agile_query`. A `@ref` that *won't* resolve is far more often a scope/structural problem (see "Diagnosing structural + warnings") than a genuine need to qualify; qualify with `wil::` only to disambiguate a name, and note that `@ref wil::com_ptr_t` + renders the qualified text `wil::com_ptr_t` rather than the tidier `com_ptr_t`. Doxygen strips a trailing `.` from a ref, + but a trailing `)` or `'s` becomes part of the target and breaks it — keep punctuation off the name. A leading `::` on an + external, undocumented name (`::Microsoft::WRL::ComPtr`) is read as an explicit link request and warns ("could not be + resolved"); drop the `::` (or `%`-escape it) to leave it as plain text. - **Namespaces.** Public entities live under `wil::`; STL-mirroring pieces live under `wistd::`. - **Prefer a `PREDEFINED` macro over a per-guard escape; use `WIL_DOXYGEN` only when needed.** Doxygen evaluates `#if` guards against `docs/Doxyfile`'s `PREDEFINED` list, which already forces many conditions true in docs — e.g. @@ -61,7 +76,10 @@ Do **not** rewrite comments purely for style, and do not document private/intern 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. + standalone `#ifdef WIL_DOXYGEN` block is for doc-only constructs with no real declaration to attach to. The **inverse**, + `#if !defined(WIL_DOXYGEN)` around a fragment, *hides* it from docs while keeping it in real builds; use it for constructs that + confuse Doxygen — notably a recursive template's own base (`struct priority_tag : priority_tag` provokes a bogus + "recursive class relation" warning), by guarding just the `: base` clause. See the example below. - **Macros are expanded for docs.** Several macros are expanded when generating documentation (`WI_NOEXCEPT` → `noexcept`, `WI_NODISCARD` → `[[nodiscard]]`, and others in `docs/Doxyfile`'s `PREDEFINED`), so document the logical signature rather than the macro-heavy source. @@ -77,7 +95,9 @@ When adding or fixing comments, verify: `_nothrow`/`NoThrow` variants — state how the function reports failure (throws, fail-fasts, or returns an `HRESULT`). 4. **Tags use `@`** (convert any `\param`, `\brief`, etc.). 5. **The brief is a real one-line summary on its own line** (the first sentence), not a restatement of the name. -6. **Cross-references resolve** — `@ref`/`@see` targets exist and are spelled correctly. +6. **Cross-references resolve** — `@ref`/`@see` targets exist and are spelled correctly, with no trailing `)`/`'s` clinging to + the name. A ref that won't resolve usually points at a scope/structural problem (see "Diagnosing structural warnings"), not a + missing `wil::` qualifier — qualify only to disambiguate. 7. **The description still matches behavior** after any signature or behavior change. 8. **Lines wrap at 130 columns** and the comment marker style matches the surrounding code. 9. **No documentation is added to private/internal members** unless they are intentionally part of the documented surface. @@ -87,6 +107,8 @@ When adding or fixing comments, verify: 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. +13. **`@{`/`@}`, `~~~` fences, and `@cond`/`@endcond` are balanced** — and no scope-closing brace (e.g. `} // namespace wil`) + is trapped inside a `@cond` region, which would leave the scope open and mis-attach every following member. ## Validating changes @@ -103,6 +125,29 @@ referencing the files you touched — mismatched parameters, undocumented public 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. +Two checks a per-file grep misses: + +- **Compare the *total* warning count before and after — not just warnings that name your file.** A structural mistake in one + header (an unbalanced `@cond`, an unclosed namespace or group) corrupts Doxygen's scope for **every** header that includes it. + It can then mask — or, once fixed, unmask — hundreds of "undocumented" warnings elsewhere, so a large swing in the global + count after a small edit is a signal, not noise. +- **Zero warnings is necessary but not sufficient — confirm visibility in the generated HTML.** A fully hidden entity produces no + warning at all, so grep the emitted HTML (or open the page) to confirm the members you documented actually render and that your + `@ref`s resolved to links. + +### Diagnosing structural warnings + +A few warnings point at a parsing problem, not the line they name: + +- **`end of file while inside a group`** (reported at the file's last line) means a group or scope opened earlier never closed + from Doxygen's view — a `@{`/`@name` missing its `@}`, an unbalanced `@cond` (below), or a runaway `~~~` fence that swallowed a + `@}`. +- **A member reported "not documented" that *already has a comment*** signals an upstream structural bug — most often a + `} // namespace …` trapped inside a `@cond` region above, leaving the namespace open so every following member is mis-scoped + and its comment never attaches. Fix the structure; do **not** add a second, redundant comment. +- Verify `@{`/`@}`, `~~~` fences, and each `@cond`/`@endcond` are balanced and that no scope-closing brace hides inside a `@cond`. + Fixing the root cause usually clears a whole cluster of downstream warnings at once. + ## Example Correcting stale parameter names and backslash tags: @@ -152,6 +197,38 @@ com_ptr make_com_ptr(T* ptr); #endif ``` +Keeping a `@cond` region brace-balanced — the closing brace of an *outer* scope must stay outside it: + +```cpp +// Wrong — `} // namespace wil` is trapped inside @cond, so Doxygen never sees namespace wil close. +namespace wil +{ +/// @cond +namespace details { /* ... */ } +} // namespace wil +/// @endcond + +// Right — @endcond comes before the outer namespace's closing brace. +namespace wil +{ +/// @cond +namespace details { /* ... */ } +/// @endcond +} // namespace wil +``` + +Hiding a recursive template's base so Doxygen doesn't emit a "recursive class relation" warning: + +```cpp +template +struct priority_tag +#if !defined(WIL_DOXYGEN) // hidden from docs, still compiled normally + : priority_tag +#endif +{ +}; +``` + ## Status This skill is an intentional starting point. The conventions above are grounded in the current headers and `docs/Doxyfile`, but From 1f995ab38e3ade4bfada7a20f81503f7f10518d1 Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 20 Jul 2026 13:41:37 -0700 Subject: [PATCH 3/4] Namespace no longer necessary --- include/wil/com.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/include/wil/com.h b/include/wil/com.h index 53d9aa95..35be6252 100644 --- a/include/wil/com.h +++ b/include/wil/com.h @@ -922,16 +922,16 @@ class com_ptr_t // Error-policy driven forms of com_ptr #ifdef WIL_ENABLE_EXCEPTIONS -//! COM pointer, errors throw exceptions (see @ref wil::com_ptr_t for details) +//! COM pointer, errors throw exceptions (see @ref com_ptr_t for details) template using com_ptr = com_ptr_t; #endif -//! COM pointer, errors return error codes (see @ref wil::com_ptr_t for details) +//! COM pointer, errors return error codes (see @ref com_ptr_t for details) template using com_ptr_nothrow = com_ptr_t; -//! COM pointer, errors fail-fast (see @ref wil::com_ptr_t for details) +//! COM pointer, errors fail-fast (see @ref com_ptr_t for details) template using com_ptr_failfast = com_ptr_t; @@ -1794,15 +1794,13 @@ inline U^ cx_dynamic_cast(T&& ptrSource) WI_NOEXCEPT #if (NTDDI_VERSION >= NTDDI_WINBLUE) #ifdef WIL_ENABLE_EXCEPTIONS -//! Agile reference to a COM interface, errors throw exceptions (see @ref wil::com_ptr_t and @ref wil::com_agile_query for -//! details) +//! Agile reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_agile_query for details) using com_agile_ref = com_ptr; #endif -//! Agile reference to a COM interface, errors return error codes (see @ref wil::com_ptr_t and @ref wil::com_agile_query_nothrow -//! for details) -using com_agile_ref_nothrow = com_ptr_nothrow; -//! Agile reference to a COM interface, errors fail fast (see @ref wil::com_ptr_t and @ref wil::com_agile_query_failfast for +//! Agile reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_agile_query_nothrow for //! details) +using com_agile_ref_nothrow = com_ptr_nothrow; +//! Agile reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_agile_query_failfast for details) using com_agile_ref_failfast = com_ptr_failfast; //! @name Create agile reference helpers @@ -1915,14 +1913,12 @@ namespace details } // namespace details #ifdef WIL_ENABLE_EXCEPTIONS -//! Weak reference to a COM interface, errors throw exceptions (see @ref wil::com_ptr_t and @ref wil::com_weak_query for details) +//! Weak reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_weak_query for details) using com_weak_ref = com_ptr; #endif -//! Weak reference to a COM interface, errors return error codes (see @ref wil::com_ptr_t and @ref wil::com_weak_query_nothrow -//! for details) +//! Weak reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_weak_query_nothrow for details) using com_weak_ref_nothrow = com_ptr_nothrow; -//! Weak reference to a COM interface, errors fail fast (see @ref wil::com_ptr_t and @ref wil::com_weak_query_failfast for -//! details) +//! Weak reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_weak_query_failfast for details) using com_weak_ref_failfast = com_ptr_failfast; //! @name Create weak reference helpers @@ -3449,14 +3445,14 @@ class com_timeout_t // Error-policy driven forms of com_timeout #ifdef WIL_ENABLE_EXCEPTIONS -//! COM timeout, errors throw exceptions (see @ref wil::com_timeout_t for details) +//! COM timeout, errors throw exceptions (see @ref com_timeout_t for details) using com_timeout = com_timeout_t; #endif -//! COM timeout, errors return error codes (see @ref wil::com_timeout_t for details) +//! COM timeout, errors return error codes (see @ref com_timeout_t for details) using com_timeout_nothrow = com_timeout_t; -//! COM timeout, errors fail-fast (see @ref wil::com_timeout_t for details) +//! COM timeout, errors fail-fast (see @ref com_timeout_t for details) using com_timeout_failfast = com_timeout_t; #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) From 11ca9f0213a512bd6abf7fa89a6aee6cb5fa120e Mon Sep 17 00:00:00 2001 From: Duncan Horn Date: Mon, 20 Jul 2026 13:59:05 -0700 Subject: [PATCH 4/4] Remove docs for stream_position_saver's defaulted constructors --- include/wil/com.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wil/com.h b/include/wil/com.h index 35be6252..6d5e431d 100644 --- a/include/wil/com.h +++ b/include/wil/com.h @@ -3062,13 +3062,13 @@ class stream_position_saver m_stream.reset(); } - //! Move constructor; transfers the saved stream position. + /// @cond stream_position_saver(stream_position_saver&&) = default; - //! Move assignment operator; transfers the saved stream position. stream_position_saver& operator=(stream_position_saver&&) = default; stream_position_saver(const stream_position_saver&) = delete; void operator=(const stream_position_saver&) = delete; + /// @endcond private: com_ptr m_stream;