From: Mark Gillard Date: Sat, 30 Apr 2022 09:15:13 +0000 (+0300) Subject: doxygen: fix keywords leaking into return type X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=2add9e0e7b18a5c7a9a6e1cfcfb8b303cadffc21;p=blog.git doxygen: fix keywords leaking into return type - fixes #225 - fixes #226 Also drive-by added support for C++20's `consteval` keyword. --- diff --git a/doc/documentation/doxygen.rst b/doc/documentation/doxygen.rst index e935aec0..be7f858f 100644 --- a/doc/documentation/doxygen.rst +++ b/doc/documentation/doxygen.rst @@ -2171,6 +2171,7 @@ Property Description :py:`func.is_conditional_noexcept` If the function is conditionally :cpp:`noexcept`. :py:`func.is_constexpr` If the function is :cpp:`constexpr` +:py:`func.is_consteval` If the function is :cpp:`consteval` :py:`func.is_defaulted` If the function is :cpp:`default`\ ed :py:`func.is_deleted` If the function is :cpp:`delete`\ d :py:`func.is_signal` If the function is a Qt signal. Set only diff --git a/documentation/doxygen.py b/documentation/doxygen.py index 5fa5d6e6..39c3a7ae 100755 --- a/documentation/doxygen.py +++ b/documentation/doxygen.py @@ -1992,39 +1992,62 @@ def parse_func(state: State, element: ET.Element): func.brief = parse_desc(state, element.find('briefdescription')) func.description, templates, params, func.return_value, func.return_values, func.exceptions, search_keywords, func.deprecated, func.since = parse_func_desc(state, element) - # Friend functions have friend as type. That's just awful. COME ON. - if func.type.startswith('friend '): - func.type = func.type[7:] - def is_identifier(a): return a == '_' or a.isalnum() # Extract function signature to prefix, suffix and various flags. Important # things affecting caller such as static or const (and rvalue overloads) # are put into signature prefix/suffix, other things to various is_* # properties. - if func.type == 'constexpr': # Constructors - func.type = '' - func.is_constexpr = True - elif func.type.startswith('constexpr'): - func.type = func.type[10:] - func.is_constexpr = True - # For some effing reason, when a constexpr function has decltype(auto) - # return type, Doxygen swaps the order of those two, causing the constexpr - # to be last. See the cpp_function_attributes test for a verification. - elif func.type.endswith('constexpr'): - func.type = func.type[:-10] - func.is_constexpr = True - else: - func.is_constexpr = False - # When 1.8.18 encounters `constexpr static`, it keeps the static there. For - # `static constexpr` it doesn't. In both cases the static="yes" is put - # there correctly. WHY DOXYGEN, WHY?! - if func.type.startswith('static'): - func.type = func.type[7:] + # + # First the prefix keywords - Doxygen has a habit of leaking attributes and + # other specifiers into the function's return type, and not necessarily + # in any consistent order (including swapping it with the actual type!) + # + # (Note that since 1.8.16 the keyword/type ordering has not been a problem, + # but this handling is left as a future-proofing mechanism.) + exposed_attribute_keywords = [ + 'constexpr', + 'consteval', + 'explicit', + 'virtual' + ] + ignored_attribute_keywords = [ + 'static', # Included in func.prefix already + 'friend', + 'inline' + ] + for kw in exposed_attribute_keywords: + setattr(func, 'is_' + kw, False) + is_static = False + matched_bad_keyword = True + while matched_bad_keyword: + matched_bad_keyword = False + for kw in exposed_attribute_keywords + ignored_attribute_keywords: + if func.type == kw: # constructors + func.type = '' + elif func.type.startswith(kw + ' '): + func.type = func.type[len(kw):].strip() + elif func.type.endswith(' ' + kw): + func.type = func.type[:len(kw)].strip() + else: + continue + matched_bad_keyword = True + if kw in exposed_attribute_keywords: + setattr(func, 'is_' + kw, True) + elif kw == 'static': + is_static = True + # Merge any leaked attributes with their corresponding XML attributes to + # account for the situation where Doxygen has only half got it right + # (which, honestly, is most of the time) + func.is_explicit = func.is_explicit or element.attrib['explicit'] == 'yes' + func.is_virtual = func.is_virtual or element.attrib['virt'] != 'non-virtual' + is_static = is_static or element.attrib['static'] == 'yes' + if 'constexpr' in element.attrib: + func.is_constexpr = func.is_constexpr or element.attrib['constexpr'] == 'yes' + if 'consteval' in element.attrib: + func.is_consteval = func.is_consteval or element.attrib['consteval'] == 'yes' func.prefix = '' - func.is_explicit = element.attrib['explicit'] == 'yes' - func.is_virtual = element.attrib['virt'] != 'non-virtual' - if element.attrib['static'] == 'yes': + if is_static: func.prefix += 'static ' # Extract additional C++11 stuff from the signature. Order matters, going # from the keywords that can be rightmost to the leftmost. diff --git a/documentation/templates/doxygen/details-func.html b/documentation/templates/doxygen/details-func.html index 5044dc95..fba88ccd 100644 --- a/documentation/templates/doxygen/details-func.html +++ b/documentation/templates/doxygen/details-func.html @@ -16,7 +16,7 @@ {% endif %} {% set j = joiner(',\n ') %} - {{ func.prefix }}{{ func.type }} {{ prefix }}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %} + {{ func.prefix }}{{ func.type }} {{ prefix }}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_consteval %} consteval{% elif func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %} {% if func.include and compound.templates == None and func.templates == None %}
#include {{ func.include[0] }}
{% endif %} diff --git a/documentation/templates/doxygen/entry-func.html b/documentation/templates/doxygen/entry-func.html index 0738fe90..263cbaf3 100644 --- a/documentation/templates/doxygen/entry-func.html +++ b/documentation/templates/doxygen/entry-func.html @@ -4,6 +4,6 @@
template<{% for t in func.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif %}{% endfor %}>
{% endif %} {% set j = joiner(',\n ') %} - {{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -> {{ func.type }}{% endif %}{% if func.deprecated %} {{ func.deprecated }}{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %} + {{ func.prefix }}{% if func.type == 'void' %}void {% elif func.type %}auto {% endif %}{{ func.name }}({% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.type and func.type != 'void' %} -> {{ func.type }}{% endif %}{% if func.deprecated %} {{ func.deprecated }}{% endif %}{% if not func.type or mark_nonpublic %}{% if func.is_protected %} protected{% if func.is_slot %} slot{% endif %}{% elif func.is_private %} private{% if func.is_slot %} slot{% endif %}{% elif func.is_signal %} signal{% elif func.is_slot %} public slot{% endif %}{% endif %}{% if func.is_defaulted %} defaulted{% endif %}{% if func.is_deleted %} deleted{% endif %}{% if func.is_explicit %} explicit {% endif %}{% if func.is_final %} final{% elif func.is_override %} override{% elif func.is_pure_virtual %} pure virtual{% elif func.is_virtual %} virtual{% endif %}{% if func.is_consteval %} consteval{% elif func.is_constexpr %} constexpr{% endif %}{% if func.is_conditional_noexcept %} noexcept(…){% elif func.is_noexcept %} noexcept{% endif %}{% if func.since %} {{ func.since }}{% endif %}
{{ func.brief }}
diff --git a/documentation/test_doxygen/cpp_friends/File.h b/documentation/test_doxygen/cpp_friends/File.h index 1b84596a..c62cb46d 100644 --- a/documentation/test_doxygen/cpp_friends/File.h +++ b/documentation/test_doxygen/cpp_friends/File.h @@ -29,6 +29,12 @@ class Class { /** @brief A friend function */ friend void friendFunction(int a, void* b); + /** @brief A 'hidden friend' operator */ + friend bool operator==(const Class&, const Class&) noexcept; + + /** @brief A constexpr 'hidden friend' operator */ + friend constexpr bool operator!=(const Class&, const Class&) noexcept; + /** @{ @name Group with friend functions */ /** @brief A friend grouped function */ diff --git a/documentation/test_doxygen/cpp_friends/classClass.html b/documentation/test_doxygen/cpp_friends/classClass.html index 6bd65852..4bf946b1 100644 --- a/documentation/test_doxygen/cpp_friends/classClass.html +++ b/documentation/test_doxygen/cpp_friends/classClass.html @@ -53,6 +53,16 @@ void* b)
A friend function.
+
+ auto operator==(const Class&, + const Class&) -> bool noexcept +
+
A 'hidden friend' operator.
+
+ auto operator!=(const Class&, + const Class&) -> bool constexpr noexcept +
+
A constexpr 'hidden friend' operator.
diff --git a/documentation/test_doxygen/cpp_function_attributes/input.h b/documentation/test_doxygen/cpp_function_attributes/input.h index cf4472b1..056ffa7f 100644 --- a/documentation/test_doxygen/cpp_function_attributes/input.h +++ b/documentation/test_doxygen/cpp_function_attributes/input.h @@ -10,6 +10,13 @@ struct Foo { */ constexpr static int constexprStaticFunction(); + /** + * @brief Consteval before static + * + * Same as above, but for C++20's consteval. + */ + consteval static int constevalStaticFunction(); + /** * @brief Constexpr after static * @@ -17,6 +24,13 @@ struct Foo { */ static constexpr int staticConstexprFunction(); + /** + * @brief Consteval after static + * + * Same as above, but for C++20's consteval. + */ + static consteval int staticConstevalFunction(); + /** * @brief Combined default and noexcept * diff --git a/documentation/test_doxygen/cpp_function_attributes/structFoo.html b/documentation/test_doxygen/cpp_function_attributes/structFoo.html index 8241d92a..2701acd5 100644 --- a/documentation/test_doxygen/cpp_function_attributes/structFoo.html +++ b/documentation/test_doxygen/cpp_function_attributes/structFoo.html @@ -43,10 +43,18 @@ static auto constexprStaticFunction() -> int constexpr
Constexpr before static.
+
+ static auto constevalStaticFunction() -> int consteval +
+
Consteval before static.
static auto staticConstexprFunction() -> int constexpr
Constexpr after static.
+
+ static auto staticConstevalFunction() -> int consteval +
+
Consteval after static.
@@ -91,6 +99,13 @@

Constexpr before static.

1.8.18 puts both constexpr and static into the return type so I have to remove them. WHY! HOW IS THAT USEFUL IN ANY WAY?!

+
+
+

+ static int Foo::constevalStaticFunction() consteval +

+

Consteval before static.

+

Same as above, but for C++20's consteval.

@@ -98,6 +113,13 @@

Constexpr after static.

In this case, static is not in the return type. FFS.

+
+
+

+ static int Foo::staticConstevalFunction() consteval +

+

Consteval after static.

+

Same as above, but for C++20's consteval.