Skip to content

Commit

Permalink
doxygen: parse override, final and conditional noexcept keywords.
Browse files Browse the repository at this point in the history
 * override and final is now parsed from function signatures, in the
   theme the [virtual] label gets replaced with either [override] or
   [final], since it doesn't make sense to include both [virtual] and
   [override]/[final] label as the former is implicit for the latter.
 * fixed order of parsing function signature keywords so function
   signatures that are both deleted/defaulted and noexcept are parsed
   correctly instead of noexcept being ignored
 * conditional noexcept is now recognized as well, shown in the theme as
   [noexcept(...)]
 * [final] class specifier is now shown in the class header and also in
   the derived class list
 * [virtual] label is now shown for both virtual base class and
   virtually derived classes to make it symmetric
  • Loading branch information
mosra committed Jan 2, 2019
1 parent b3f5de7 commit 2e2b57f
Show file tree
Hide file tree
Showing 17 changed files with 667 additions and 72 deletions.
137 changes: 76 additions & 61 deletions doc/doxygen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,8 @@ Property Description
:py:`compound.footer_navigation` Footer navigation of a page. See
`Navigation properties`_ for details.
:py:`compound.brief` Brief description. Can be empty. [1]_
:py:`compound.is_final` Whether the class is :cpp:`final`. Set
only for classes.
:py:`compound.is_deprecated` Whether the compound is deprecated. [7]_
:py:`compound.description` Detailed description. Can be empty. [2]_
:py:`compound.modules` List of submodules in this compound.
Expand Down Expand Up @@ -1636,8 +1638,11 @@ Property Description
:py:`class.is_deprecated` Whether the class is deprecated. [7]_
:py:`class.is_protected` Whether this is a protected base class. Set only
for base classes.
:py:`class.is_virtual` Whether this is a virtual base class. Set only for
base classes.
:py:`class.is_virtual` Whether this is a virtual base class or a
virtually derived class. Set only for base /
derived classes.
:py:`class.is_final` Whether this is a final derived class. Set only for
derived classes.
=========================== ===================================================
`Enum properties`_
Expand Down Expand Up @@ -1743,65 +1748,75 @@ a list of functions, where every item has the following properties:
.. class:: m-table m-fullwidth
=============================== ===============================================
Property Description
=============================== ===============================================
:py:`func.base_url` Base URL of file containing detailed
description [3]_
:py:`func.include` Corresponding :cpp:`#include` to get the
function declaration. Present only for
functions inside modules or inside namespaces
that are spread over multiple files. See
`Include properties`_ for more information.
:py:`func.id` Identifier hash [3]_
:py:`func.type` Function return type [6]_
:py:`func.name` Function name [4]_
:py:`func.templates` Template specification. See
`Template properties`_ for details.
:py:`func.has_template_details` If template parameters have description
:py:`func.params` List of function parameters. See below for
details.
:py:`func.has_param_details` If function parameters have description
:py:`func.return_value` Return value description. Can be empty.
:py:`func.return_values` Description of particular return values. See
below for details.
:py:`func.exceptions` Description of particular exception types. See
below for details.
:py:`func.brief` Brief description. Can be empty. [1]_
:py:`func.description` Detailed description. Can be empty. [2]_
:py:`func.has_details` If there is enough content for the full
description block [5]_
:py:`func.prefix` Function signature prefix, containing keywords
such as :cpp:`static`. Information about
:cpp:`constexpr`\ ness, :cpp:`explicit`\ ness
and :cpp:`virtual`\ ity is removed from the
prefix and available via other properties.
:py:`func.suffix` Function signature suffix, containing keywords
such as :cpp:`const` and r-value overloads.
Information about :cpp:`noexcept`, pure
:cpp:`virtual`\ ity and :cpp:`delete`\ d /
:cpp:`default`\ ed functions is removed from
the suffix and available via other properties.
:py:`func.is_deprecated` Whether the function is deprecated. [7]_
:py:`func.is_protected` If the function is :cpp:`protected`. Set only
for member functions.
:py:`func.is_private` If the function is :cpp:`private`. Set only for
member functions.
:py:`func.is_explicit` If the function is :cpp:`explicit`. Set only
for member functions.
:py:`func.is_virtual` If the function is :cpp:`virtual`. Set only for
member functions.
:py:`func.is_pure_virtual` If the function is pure :cpp:`virtual`. Set
only for member functions.
:py:`func.is_noexcept` If the function is :cpp:`noexcept`
:py:`func.is_constexpr` If the function is :cpp:`constexpr`
: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 for
member functions.
:py:`func.is_slot` If the function is a Qt slot. Set only for
member functions.
=============================== ===============================================
=================================== ===========================================
Property Description
=================================== ===========================================
:py:`func.base_url` Base URL of file containing detailed
description [3]_
:py:`func.include` Corresponding :cpp:`#include` to get the
function declaration. Present only for
functions inside modules or inside
namespaces that are spread over multiple
files. See `Include properties`_ for more
information.
:py:`func.id` Identifier hash [3]_
:py:`func.type` Function return type [6]_
:py:`func.name` Function name [4]_
:py:`func.templates` Template specification. See
`Template properties`_ for details.
:py:`func.has_template_details` If template parameters have description
:py:`func.params` List of function parameters. See below for
details.
:py:`func.has_param_details` If function parameters have description
:py:`func.return_value` Return value description. Can be empty.
:py:`func.return_values` Description of particular return values.
See below for details.
:py:`func.exceptions` Description of particular exception types.
See below for details.
:py:`func.brief` Brief description. Can be empty. [1]_
:py:`func.description` Detailed description. Can be empty. [2]_
:py:`func.has_details` If there is enough content for the full
description block [5]_
:py:`func.prefix` Function signature prefix, containing
keywords such as :cpp:`static`. Information
about :cpp:`constexpr`\ ness,
:cpp:`explicit`\ ness and
:cpp:`virtual`\ ity is removed from the
prefix and available via other properties.
:py:`func.suffix` Function signature suffix, containing
keywords such as :cpp:`const` and r-value
overloads. Information about
:cpp:`noexcept`, pure :cpp:`virtual`\ ity
and :cpp:`delete`\ d / :cpp:`default`\ ed
functions is removed from the suffix and
available via other properties.
:py:`func.is_deprecated` Whether the function is deprecated. [7]_
:py:`func.is_protected` If the function is :cpp:`protected`. Set
only for member functions.
:py:`func.is_private` If the function is :cpp:`private`. Set only
for member functions.
:py:`func.is_explicit` If the function is :cpp:`explicit`. Set
only for member functions.
:py:`func.is_virtual` If the function is :cpp:`virtual` (or pure
virtual). Set only for member functions.
:py:`func.is_pure_virtual` If the function is pure :cpp:`virtual`. Set
only for member functions.
:py:`func.is_override` If the function is an :cpp:`override`. Set
only for member functions.
:py:`func.is_final` If the function is a :cpp:`final override`.
Set only for member functions.
:py:`func.is_noexcept` If the function is :cpp:`noexcept` (even
conditionally)
: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_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
for member functions.
:py:`func.is_slot` If the function is a Qt slot. Set only for
member functions.
=================================== ===========================================
The :py:`func.params` is a list of function parameters and their description.
Each item has the following properties:
Expand Down
52 changes: 46 additions & 6 deletions doxygen/dox2html5.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ def __init__(self):
self.url: str
self.brief: str
self.has_details: bool
self.is_deprecated: bool
self.is_final: bool = None
self.children: List[str]
self.parent: str = None

Expand Down Expand Up @@ -1790,12 +1792,9 @@ def parse_func(state: State, element: ET.Element):
func.is_virtual = element.attrib['virt'] != 'non-virtual'
if element.attrib['static'] == 'yes':
func.prefix += 'static '
signature = element.find('argsstring').text
if signature.endswith(' noexcept'):
signature = signature[:-9]
func.is_noexcept = True
else:
func.is_noexcept = False
# Extract additional C++11 stuff from the signature. Order matters, going
# from the keywords that can be rightmost to the leftmost.
signature: str = element.find('argsstring').text
if signature.endswith('=default'):
signature = signature[:-8]
func.is_defaulted = True
Expand All @@ -1811,6 +1810,32 @@ def parse_func(state: State, element: ET.Element):
func.is_pure_virtual = True
else:
func.is_pure_virtual = False
# Final tested twice because it can be both `override final`
func.is_final = False
if signature.endswith(' final'):
signature = signature[:-6]
func.is_final = True
if signature.endswith(' override'):
signature = signature[:-9]
func.is_override = True
else:
func.is_override = False
# ... and `final override`
if signature.endswith(' final'):
signature = signature[:-6]
func.is_final = True
if signature.endswith(' noexcept'):
signature = signature[:-9]
func.is_noexcept = True
func.is_conditional_noexcept = False
elif ' noexcept(' in signature:
signature = signature[:signature.index(' noexcept(')]
func.is_noexcept = True
func.is_conditional_noexcept = True
else:
func.is_noexcept = False
func.is_conditional_noexcept = False
# Put the rest (const, volatile, ...) into a suffix
func.suffix = html.escape(signature[signature.rindex(')') + 1:].strip())
if func.suffix: func.suffix = ' ' + func.suffix
# Protected / private makes no sense for friend functions
Expand Down Expand Up @@ -1995,6 +2020,7 @@ def extract_metadata(state: State, xml):
compound.has_details = compound.kind in ['group', 'page'] or compound.brief or compounddef.find('detaileddescription')
compound.children = []

# Deprecation status
compound.is_deprecated = False
for i in compounddef.find('detaileddescription').findall('.//xrefsect'):
id = i.attrib['id']
Expand All @@ -2004,6 +2030,12 @@ def extract_metadata(state: State, xml):
compound.is_deprecated = True
break

# Final classes
if compound.kind in ['struct', 'class', 'union'] and compounddef.attrib.get('final') == 'yes':
compound.is_final = True
else:
compound.is_final = False

if compound.kind in ['class', 'struct', 'union']:
# Fix type spacing
compound.name = fix_type_spacing(compound.name)
Expand Down Expand Up @@ -2360,6 +2392,12 @@ def parse_xml(state: State, xml: str):
else:
state.current_prefix = []

# Final classes
if compound.kind in ['struct', 'class', 'union'] and compounddef.attrib.get('final') == 'yes':
compound.is_final = True
else:
compound.is_final = False

# Decide about the include file for this compound. Classes get it always,
# namespaces without any members too.
state.current_kind = compound.kind
Expand Down Expand Up @@ -2537,6 +2575,8 @@ def parse_xml(state: State, xml: str):
class_.brief = symbol.brief
class_.templates = symbol.templates
class_.is_deprecated = symbol.is_deprecated
class_.is_virtual = compounddef_child.attrib['virt'] == 'virtual'
class_.is_final = symbol.is_final

compound.derived_classes += [class_]

Expand Down
4 changes: 3 additions & 1 deletion doxygen/templates/base-class-reference.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ <h1>
{% set j = joiner(', ') %}
<div class="m-dox-template">template&lt;{% for t in compound.templates %}{{ j() }}{{ t.type }}{% if t.name %} {{ t.name }}{% endif %}{% if t.default %} = {{ t.default }}{% endif%}{% endfor %}&gt;</div>
{% endif %}
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>::<wbr/></span>{% endfor %}{{ compound.breadcrumb[-1][0] }} <span class="m-thin">{{ compound.kind }}</span>
{%+ for name, target in compound.breadcrumb[:-1] %}<span class="m-breadcrumb"><a href="{{ target }}">{{ name }}</a>::<wbr/></span>{% endfor %}{{ compound.breadcrumb[-1][0] }} <span class="m-thin">{{ compound.kind }}</span>{% if compound.is_final %} <span class="m-label m-flat m-warning">final</span>{% endif %}
{# need an explicit space here otherwise the newline gets removed #}

{% if compound.include and compound.templates == None %}
<div class="m-dox-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ compound.include[1] }}">{{ compound.include[0] }}</a></div>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion doxygen/templates/details-func.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ <h3>
</div>
{% endif %}
{% set j = joiner(',\n ') %}
<span class="m-dox-wrap-bumper">{{ func.prefix }}{{ func.type }} {{ prefix }}</span><span class="m-dox-wrap"><span class="m-dox-wrap-bumper"><a href="#{{ func.id }}" class="m-dox-self">{{ func.name }}</a>(</span><span class="m-dox-wrap">{% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} <span class="m-label m-info">explicit</span> {% endif %}{% if func.is_pure_virtual %} <span class="m-label m-warning">pure virtual</span>{% elif func.is_virtual %} <span class="m-label m-warning">virtual</span>{% endif %}{% if func.is_protected %} <span class="m-label m-warning">protected{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_private %} <span class="m-label m-danger">private{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_signal %} <span class="m-label m-success">signal</span>{% elif func.is_slot %} <span class="m-label m-success">public slot</span>{% endif %}{% if func.is_defaulted %} <span class="m-label m-info">defaulted</span>{% endif %}{% if func.is_deleted %} <span class="m-label m-danger">deleted</span>{% endif %}{% if func.is_constexpr %} <span class="m-label m-primary">constexpr</span>{% endif %}{% if func.is_noexcept %} <span class="m-label m-success">noexcept</span>{% endif %}</span></span>
<span class="m-dox-wrap-bumper">{{ func.prefix }}{{ func.type }} {{ prefix }}</span><span class="m-dox-wrap"><span class="m-dox-wrap-bumper"><a href="#{{ func.id }}" class="m-dox-self">{{ func.name }}</a>(</span><span class="m-dox-wrap">{% for param in func.params %}{{ j() }}{{ param.type_name }}{% if param.default %} = {{ param.default }}{% endif %}{% endfor %}){{ func.suffix }}{% if func.is_explicit %} <span class="m-label m-info">explicit</span> {% endif %}{% if func.is_final %} <span class="m-label m-warning">final</span>{% elif func.is_override %} <span class="m-label m-warning">override</span>{% elif func.is_pure_virtual %} <span class="m-label m-warning">pure virtual</span>{% elif func.is_virtual %} <span class="m-label m-warning">virtual</span>{% endif %}{% if func.is_protected %} <span class="m-label m-warning">protected{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_private %} <span class="m-label m-danger">private{% if func.is_slot %} slot{% endif %}</span>{% elif func.is_signal %} <span class="m-label m-success">signal</span>{% elif func.is_slot %} <span class="m-label m-success">public slot</span>{% endif %}{% if func.is_defaulted %} <span class="m-label m-info">defaulted</span>{% endif %}{% if func.is_deleted %} <span class="m-label m-danger">deleted</span>{% endif %}{% if func.is_constexpr %} <span class="m-label m-primary">constexpr</span>{% endif %}{% if func.is_conditional_noexcept %} <span class="m-label m-success">noexcept(…)</span>{% elif func.is_noexcept %} <span class="m-label m-success">noexcept</span>{% endif %}</span></span>
{% if func.include and compound.templates == None and func.templates == None %}
<div class="m-dox-include m-code m-inverted m-text-right"><span class="cp">#include</span> <a class="cpf" href="{{ func.include[1] }}">{{ func.include[0] }}</a></div>
{% endif %}
Expand Down
Loading

0 comments on commit 2e2b57f

Please sign in to comment.