Repository navigation
Hide noexcept function_traits specializations from Doxygen - #3367
Merged
Merged
Conversation
The noexcept specializations added in ros2#3322 make the rosdoc2 documentation build fail: Sphinx's C++ domain hits an internal AssertionError while resolving references in the function_traits specializations. Wrap the noexcept variants in \cond/\endcond so Doxygen skips them. They mirror the documented non-noexcept specializations, and the compiled code is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: R Kent James <kent@caspia.com>
mjcarroll
approved these changes
Oct 20, 2026
Member
|
Just a documentation change and passes |
ABI Compliance Check✅ Verdict: compatible
✅
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The noexcept specializations added in #3322 make the rosdoc2 documentation build fail: Sphinx's C++ domain hits an internal AssertionError while resolving references in the function_traits specializations. Wrap the noexcept variants in \cond/\endcond so Doxygen skips them. They mirror the documented non-noexcept specializations, and the compiled code is unchanged.
Is this user-facing behavior change?
No, affects documentation only.
Did you use Generative AI?
Yes, Opus 5.5
More details as reported by Opus:
These lines are there to keep Doxygen from documenting the two noexcept specializations. Without them, the rosdoc2 build of rclcpp crashes in Sphinx.
What breaks without it
The unfixed run (runs_rolling/rclcpp/log.txt) gets through Doxygen. Then Sphinx dies while resolving cross-references:
File ".../sphinx/domains/cpp/init.py", line 1115, in _resolve_xref_inner
assert parent_symbol # should be there
AssertionError
The patched run (runs_rolling/fix_rclcpp/log.txt) ends with build succeeded.
Why noexcept is the trigger
In C++17, noexcept became part of a function's type. So R(Args...) and R(Args...) noexcept are different types, and function_traits needs separate partial specializations for each. That's the point of the comment in the code.
Doxygen sees four separate specializations and outputs four separate class pages. The log shows entries like function_traits< ReturnTypeT(Args ...) noexcept >.
Breathe/Exhale turns each page into a Sphinx cpp:struct declaration. Sphinx's C++ domain doesn't handle noexcept inside a function type used as a template argument. In its symbol table, the noexcept specialization ends up colliding with (or failing to attach to) the plain one. When Sphinx later resolves a reference to it, the parent symbol it expects isn't there, and an internal assert fails. Because it's an assert, it doesn't just log a warning like the "Duplicate C++ declaration" messages elsewhere in the log; it aborts the whole build.
To be clear about the evidence: the traceback and the before/after runs are confirmed. The exact way Sphinx mishandles noexcept is my best reading. The log has no warning naming these structs before the assert.
Why \cond / \endcond
/// \cond … /// \endcond is Doxygen's way to skip a block. Everything between the markers is left out of the docs, but the compiler still sees it, so the code behaves exactly the same. The only loss is two doc pages nobody needs: these specializations just inherit from the non-noexcept versions, which are still documented.
It's the same idea as the DOXYGEN_ONLY trick a few lines further down in function_traits.hpp. That one shows Doxygen a simpler declaration; this one hides the problem declarations completely.