From de1cd95a75dc098fc0c81483b823b2bcea055079 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 17 Jan 2021 13:45:49 +0100 Subject: [PATCH 1/5] C, fixes for alias directive --- CHANGES | 3 +++ sphinx/domains/c.py | 23 +++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 91aacb9a436..d686d0e31f6 100644 --- a/CHANGES +++ b/CHANGES @@ -148,6 +148,9 @@ Bugs fixed builds * #8865: LaTeX: Restructure the index nodes inside title nodes only on LaTeX builds +* C, :rst:dir:`c:alias` skip symbols without explicit declarations + instead of crashing. +* C, :rst:dir:`c:alias` give a warning when the root symbol is not declared. Testing -------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 5336b003c89..c4ebdaf3738 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3459,9 +3459,9 @@ def copy(self) -> 'AliasNode': class AliasTransform(SphinxTransform): default_priority = ReferencesResolver.default_priority - 1 - def _render_symbol(self, s: Symbol, maxdepth: int, document: Any) -> List[Node]: + def _render_symbol(self, s: Symbol, maxdepth: int, + options: dict, document: Any) -> List[Node]: nodes = [] # type: List[Node] - options = dict() # type: ignore signode = addnodes.desc_signature('', '') nodes.append(signode) s.declaration.describe_signature(signode, 'markName', self.env, options) @@ -3483,7 +3483,9 @@ def _render_symbol(self, s: Symbol, maxdepth: int, document: Any) -> List[Node]: desc['noindex'] = True for sChild in s.children: - childNodes = self._render_symbol(sChild, maxdepth, document) + if sChild.declaration is None: + continue + childNodes = self._render_symbol(sChild, maxdepth, options, document) desc.extend(childNodes) if len(desc.children) != 0: @@ -3531,8 +3533,21 @@ def apply(self, **kwargs: Any) -> None: location=node) node.replace_self(signode) continue + if s.declaration is None: + signode = addnodes.desc_signature(sig, '') + node.append(signode) + signode.clear() + signode += addnodes.desc_name(sig, sig) + + logger.warning( + "Can not render C declaration for alias '%s'. No such declaration." % name, + location=node) + node.replace_self(signode) + continue - nodes = self._render_symbol(s, maxdepth=node.maxdepth, document=node.document) + options = dict() # type: ignore + nodes = self._render_symbol(s, maxdepth=node.maxdepth, + options=options, document=node.document) node.replace_self(nodes) From d0a4d0147e6408e5e34393381aad11f197cd291c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 17 Jan 2021 13:46:35 +0100 Subject: [PATCH 2/5] C++, add recursiveness to alias Fixes sphinx-doc/sphinx#8213 in the most basic way. --- CHANGES | 2 + doc/usage/restructuredtext/domains.rst | 11 +++++ sphinx/domains/cpp.py | 56 ++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index d686d0e31f6..758fb80e099 100644 --- a/CHANGES +++ b/CHANGES @@ -76,6 +76,8 @@ Features added * C++, also hyperlink operator overloads in expressions and alias declarations. * #8247: Allow production lists to refer to tokens from other production groups * #8813: Show what extension (or module) caused it on errors on event handler +* #8213: C++: add ``maxdepth`` option to :rst:dir:`cpp:alias` to insert nested + declarations. Bugs fixed ---------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index f3754ab7c20..279d0f4017d 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1179,6 +1179,17 @@ The following directive can be used for this purpose. .. versionadded:: 2.0 + .. rubric:: Options + + .. rst:directive:option:: maxdepth: int + + Insert nested declarations as well, up to the total depth given. + Use 0 for infinite depth and 1 for just the mentioned declaration. + Defaults to 1. + + .. versionadded:: 3.5 + + Constrained Templates ~~~~~~~~~~~~~~~~~~~~~ diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 25e6f1421b6..50c7ab12cdb 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3742,6 +3742,7 @@ def describe_signature(self, signode: desc_signature, mode: str, elif self.objectType == 'enumerator': mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') else: + print(self.objectType) assert False self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) lastDeclNode = mainDeclNode @@ -7046,10 +7047,12 @@ def run(self) -> List[Node]: class AliasNode(nodes.Element): - def __init__(self, sig: str, env: "BuildEnvironment" = None, + def __init__(self, sig: str, maxdepth: int, env: "BuildEnvironment" = None, parentKey: LookupKey = None) -> None: super().__init__() self.sig = sig + self.maxdepth = maxdepth + assert maxdepth >= 0 if env is not None: if 'cpp:parent_symbol' not in env.temp_data: root = env.domaindata['cpp']['root_symbol'] @@ -7060,12 +7063,47 @@ def __init__(self, sig: str, env: "BuildEnvironment" = None, self.parentKey = parentKey def copy(self) -> 'AliasNode': - return self.__class__(self.sig, env=None, parentKey=self.parentKey) + return self.__class__(self.sig, self.maxdepth, env=None, parentKey=self.parentKey) class AliasTransform(SphinxTransform): default_priority = ReferencesResolver.default_priority - 1 + def _render_symbol(self, s: Symbol, maxdepth: int, + options: dict, document: Any) -> List[Node]: + nodes = [] # type: List[Node] + signode = addnodes.desc_signature('', '') + nodes.append(signode) + s.declaration.describe_signature(signode, 'markName', self.env, options) + if maxdepth == 0: + recurse = True + elif maxdepth == 1: + recurse = False + else: + maxdepth -= 1 + recurse = True + if recurse: + content = addnodes.desc_content() + desc = addnodes.desc() + content.append(desc) + desc.document = document + desc['domain'] = 'cpp' + # 'desctype' is a backwards compatible attribute + desc['objtype'] = desc['desctype'] = 'alias' + desc['noindex'] = True + + for sChild in s._children: + if sChild.declaration is None: + continue + if sChild.declaration.objectType in ("templateParam", "functionParam"): + continue + childNodes = self._render_symbol(sChild, maxdepth, options, document) + desc.extend(childNodes) + + if len(desc.children) != 0: + nodes.append(content) + return nodes + def apply(self, **kwargs: Any) -> None: for node in self.document.traverse(AliasNode): sig = node.sig @@ -7139,14 +7177,17 @@ def apply(self, **kwargs: Any) -> None: options = dict() options['tparam-line-spec'] = False for s in symbols: - signode = addnodes.desc_signature(sig, '') - nodes.append(signode) - s.declaration.describe_signature(signode, 'markName', self.env, options) + assert s.declaration is not None + res = self._render_symbol(s, maxdepth=node.maxdepth, + options=options, document=node.document) + nodes.extend(res) node.replace_self(nodes) class CPPAliasObject(ObjectDescription): - option_spec = {} # type: Dict + option_spec = { + 'maxdepth': directives.nonnegative_int + } # type: Dict def run(self) -> List[Node]: """ @@ -7166,9 +7207,10 @@ def run(self) -> List[Node]: node['objtype'] = node['desctype'] = self.objtype self.names = [] # type: List[str] + maxdepth = self.options.get('maxdepth', 1) signatures = self.get_signatures() for i, sig in enumerate(signatures): - node.append(AliasNode(sig, env=self.env)) + node.append(AliasNode(sig, maxdepth, env=self.env)) contentnode = addnodes.desc_content() node.append(contentnode) From 06a956200b4c2b022bf6c16027954daf887838ae Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 17 Jan 2021 20:01:10 +0100 Subject: [PATCH 3/5] C, add noroot option to alias directive --- CHANGES | 2 + doc/usage/restructuredtext/domains.rst | 7 +++ sphinx/domains/c.py | 87 +++++++++++++++++--------- 3 files changed, 68 insertions(+), 28 deletions(-) diff --git a/CHANGES b/CHANGES index 758fb80e099..3bc99f51b84 100644 --- a/CHANGES +++ b/CHANGES @@ -78,6 +78,8 @@ Features added * #8813: Show what extension (or module) caused it on errors on event handler * #8213: C++: add ``maxdepth`` option to :rst:dir:`cpp:alias` to insert nested declarations. +* C, add ``noroot`` option to :rst:dir:`c:alias` to render only nested + declarations. Bugs fixed ---------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 279d0f4017d..1a8affe568f 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -755,6 +755,13 @@ The following directive can be used for this purpose. .. versionadded:: 3.3 + .. rst:directive:option:: noroot + + Skip the mentioned declarations and only render nested declarations. + Requires ``maxdepth`` either 0 or at least 2. + + .. versionadded:: 3.5 + .. c:namespace-pop:: diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index c4ebdaf3738..2030aab61f0 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3435,12 +3435,12 @@ def run(self) -> List[Node]: class AliasNode(nodes.Element): - def __init__(self, sig: str, maxdepth: int, document: Any, env: "BuildEnvironment" = None, + def __init__(self, sig: str, aliasOptions: dict, + document: Any, env: "BuildEnvironment" = None, parentKey: LookupKey = None) -> None: super().__init__() self.sig = sig - self.maxdepth = maxdepth - assert maxdepth >= 0 + self.aliasOptions = aliasOptions self.document = document if env is not None: if 'c:parent_symbol' not in env.temp_data: @@ -3452,19 +3452,16 @@ def __init__(self, sig: str, maxdepth: int, document: Any, env: "BuildEnvironmen self.parentKey = parentKey def copy(self) -> 'AliasNode': - return self.__class__(self.sig, self.maxdepth, self.document, + return self.__class__(self.sig, self.aliasOptions, self.document, env=None, parentKey=self.parentKey) class AliasTransform(SphinxTransform): default_priority = ReferencesResolver.default_priority - 1 - def _render_symbol(self, s: Symbol, maxdepth: int, - options: dict, document: Any) -> List[Node]: - nodes = [] # type: List[Node] - signode = addnodes.desc_signature('', '') - nodes.append(signode) - s.declaration.describe_signature(signode, 'markName', self.env, options) + def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool, + aliasOptions: dict, renderOptions: dict, + document: Any) -> List[Node]: if maxdepth == 0: recurse = True elif maxdepth == 1: @@ -3472,28 +3469,43 @@ def _render_symbol(self, s: Symbol, maxdepth: int, else: maxdepth -= 1 recurse = True + + nodes = [] # type: List[Node] + if not skipThis: + signode = addnodes.desc_signature('', '') + nodes.append(signode) + s.declaration.describe_signature(signode, 'markName', self.env, renderOptions) + if recurse: - content = addnodes.desc_content() - desc = addnodes.desc() - content.append(desc) - desc.document = document - desc['domain'] = 'c' - # 'desctype' is a backwards compatible attribute - desc['objtype'] = desc['desctype'] = 'alias' - desc['noindex'] = True + if skipThis: + childContainer = nodes + else: + content = addnodes.desc_content() + desc = addnodes.desc() + content.append(desc) + desc.document = document + desc['domain'] = 'c' + # 'desctype' is a backwards compatible attribute + desc['objtype'] = desc['desctype'] = 'alias' + desc['noindex'] = True + childContainer = desc for sChild in s.children: if sChild.declaration is None: continue - childNodes = self._render_symbol(sChild, maxdepth, options, document) - desc.extend(childNodes) + childNodes = self._render_symbol( + sChild, maxdepth=maxdepth, skipThis=False, + aliasOptions=aliasOptions, renderOptions=renderOptions, + document=document) + childContainer.extend(childNodes) - if len(desc.children) != 0: + if not skipThis and len(desc.children) != 0: nodes.append(content) return nodes def apply(self, **kwargs: Any) -> None: for node in self.document.traverse(AliasNode): + node = cast(AliasNode, node) sig = node.sig parentKey = node.parentKey try: @@ -3533,7 +3545,10 @@ def apply(self, **kwargs: Any) -> None: location=node) node.replace_self(signode) continue - if s.declaration is None: + # Declarations like .. var:: int Missing::var + # may introduce symbols without declarations. + # But if we skip the root then it is ok to start recursion from it. + if not node.aliasOptions['noroot'] and s.declaration is None: signode = addnodes.desc_signature(sig, '') node.append(signode) signode.clear() @@ -3545,18 +3560,25 @@ def apply(self, **kwargs: Any) -> None: node.replace_self(signode) continue - options = dict() # type: ignore - nodes = self._render_symbol(s, maxdepth=node.maxdepth, - options=options, document=node.document) + nodes = self._render_symbol(s, maxdepth=node.aliasOptions['maxdepth'], + skipThis=node.aliasOptions['noroot'], + aliasOptions=node.aliasOptions, + renderOptions=dict(), document=node.document) node.replace_self(nodes) class CAliasObject(ObjectDescription): option_spec = { - 'maxdepth': directives.nonnegative_int + 'maxdepth': directives.nonnegative_int, + 'noroot': directives.flag, } # type: Dict def run(self) -> List[Node]: + """ + On purpose this doesn't call the ObjectDescription version, but is based on it. + Each alias signature may expand into multiple real signatures if 'noroot'. + The code is therefore based on the ObjectDescription version. + """ if ':' in self.name: self.domain, self.objtype = self.name.split(':', 1) else: @@ -3570,10 +3592,19 @@ def run(self) -> List[Node]: node['noindex'] = True self.names = [] # type: List[str] - maxdepth = self.options.get('maxdepth', 1) + aliasOptions = { + 'maxdepth': self.options.get('maxdepth', 1), + 'noroot': 'noroot' in self.options, + } + if aliasOptions['noroot'] and aliasOptions['maxdepth'] == 1: + logger.warning("Error in C alias declaration." + " Requested 'noroot' but 'maxdepth' 1." + " When skipping the root declaration," + " need 'maxdepth' 0 for infinite or at least 2.", + location=self.get_source_info()) signatures = self.get_signatures() for i, sig in enumerate(signatures): - node.append(AliasNode(sig, maxdepth, self.state.document, env=self.env)) + node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env)) return [node] From a7fcc156835eaab404bed56bc8bcb65d55ed89fa Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 17 Jan 2021 21:59:01 +0100 Subject: [PATCH 4/5] C++, add noroot option to alias directive --- CHANGES | 2 + doc/usage/restructuredtext/domains.rst | 7 ++ sphinx/domains/cpp.py | 88 +++++++++++++++++--------- 3 files changed, 67 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index 3bc99f51b84..905b10f3bc2 100644 --- a/CHANGES +++ b/CHANGES @@ -80,6 +80,8 @@ Features added declarations. * C, add ``noroot`` option to :rst:dir:`c:alias` to render only nested declarations. +* C++, add ``noroot`` option to :rst:dir:`cpp:alias` to render only nested + declarations. Bugs fixed ---------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 1a8affe568f..e4909f7930d 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1196,6 +1196,13 @@ The following directive can be used for this purpose. .. versionadded:: 3.5 + .. rst:directive:option:: noroot + + Skip the mentioned declarations and only render nested declarations. + Requires ``maxdepth`` either 0 or at least 2. + + .. versionadded:: 3.5 + Constrained Templates ~~~~~~~~~~~~~~~~~~~~~ diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 50c7ab12cdb..9d94f651a11 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -10,7 +10,7 @@ import re from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type, - TypeVar, Union) + TypeVar, Union, cast) from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message @@ -7047,12 +7047,12 @@ def run(self) -> List[Node]: class AliasNode(nodes.Element): - def __init__(self, sig: str, maxdepth: int, env: "BuildEnvironment" = None, + def __init__(self, sig: str, aliasOptions: dict, + env: "BuildEnvironment" = None, parentKey: LookupKey = None) -> None: super().__init__() self.sig = sig - self.maxdepth = maxdepth - assert maxdepth >= 0 + self.aliasOptions = aliasOptions if env is not None: if 'cpp:parent_symbol' not in env.temp_data: root = env.domaindata['cpp']['root_symbol'] @@ -7063,18 +7063,16 @@ def __init__(self, sig: str, maxdepth: int, env: "BuildEnvironment" = None, self.parentKey = parentKey def copy(self) -> 'AliasNode': - return self.__class__(self.sig, self.maxdepth, env=None, parentKey=self.parentKey) + return self.__class__(self.sig, self.aliasOptions, + env=None, parentKey=self.parentKey) class AliasTransform(SphinxTransform): default_priority = ReferencesResolver.default_priority - 1 - def _render_symbol(self, s: Symbol, maxdepth: int, - options: dict, document: Any) -> List[Node]: - nodes = [] # type: List[Node] - signode = addnodes.desc_signature('', '') - nodes.append(signode) - s.declaration.describe_signature(signode, 'markName', self.env, options) + def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool, + aliasOptions: dict, renderOptions: dict, + document: Any) -> List[Node]: if maxdepth == 0: recurse = True elif maxdepth == 1: @@ -7082,30 +7080,45 @@ def _render_symbol(self, s: Symbol, maxdepth: int, else: maxdepth -= 1 recurse = True + + nodes = [] # type: List[Node] + if not skipThis: + signode = addnodes.desc_signature('', '') + nodes.append(signode) + s.declaration.describe_signature(signode, 'markName', self.env, renderOptions) + if recurse: - content = addnodes.desc_content() - desc = addnodes.desc() - content.append(desc) - desc.document = document - desc['domain'] = 'cpp' - # 'desctype' is a backwards compatible attribute - desc['objtype'] = desc['desctype'] = 'alias' - desc['noindex'] = True + if skipThis: + childContainer = nodes + else: + content = addnodes.desc_content() + desc = addnodes.desc() + content.append(desc) + desc.document = document + desc['domain'] = 'cpp' + # 'desctype' is a backwards compatible attribute + desc['objtype'] = desc['desctype'] = 'alias' + desc['noindex'] = True + childContainer = desc for sChild in s._children: if sChild.declaration is None: continue if sChild.declaration.objectType in ("templateParam", "functionParam"): continue - childNodes = self._render_symbol(sChild, maxdepth, options, document) - desc.extend(childNodes) + childNodes = self._render_symbol( + sChild, maxdepth=maxdepth, skipThis=False, + aliasOptions=aliasOptions, renderOptions=renderOptions, + document=document) + childContainer.extend(childNodes) - if len(desc.children) != 0: + if not skipThis and len(desc.children) != 0: nodes.append(content) return nodes def apply(self, **kwargs: Any) -> None: for node in self.document.traverse(AliasNode): + node = cast(AliasNode, node) sig = node.sig parentKey = node.parentKey try: @@ -7169,24 +7182,30 @@ def apply(self, **kwargs: Any) -> None: signode.clear() signode += addnodes.desc_name(sig, sig) - logger.warning("Could not find C++ declaration for alias '%s'." % ast, + logger.warning("Can not find C++ declaration for alias '%s'." % ast, location=node) node.replace_self(signode) else: nodes = [] - options = dict() - options['tparam-line-spec'] = False + renderOptions = { + 'tparam-line-spec': False, + } for s in symbols: assert s.declaration is not None - res = self._render_symbol(s, maxdepth=node.maxdepth, - options=options, document=node.document) + res = self._render_symbol( + s, maxdepth=node.aliasOptions['maxdepth'], + skipThis=node.aliasOptions['noroot'], + aliasOptions=node.aliasOptions, + renderOptions=renderOptions, + document=node.document) nodes.extend(res) node.replace_self(nodes) class CPPAliasObject(ObjectDescription): option_spec = { - 'maxdepth': directives.nonnegative_int + 'maxdepth': directives.nonnegative_int, + 'noroot': directives.flag, } # type: Dict def run(self) -> List[Node]: @@ -7207,10 +7226,19 @@ def run(self) -> List[Node]: node['objtype'] = node['desctype'] = self.objtype self.names = [] # type: List[str] - maxdepth = self.options.get('maxdepth', 1) + aliasOptions = { + 'maxdepth': self.options.get('maxdepth', 1), + 'noroot': 'noroot' in self.options, + } + if aliasOptions['noroot'] and aliasOptions['maxdepth'] == 1: + logger.warning("Error in C++ alias declaration." + " Requested 'noroot' but 'maxdepth' 1." + " When skipping the root declaration," + " need 'maxdepth' 0 for infinite or at least 2.", + location=self.get_source_info()) signatures = self.get_signatures() for i, sig in enumerate(signatures): - node.append(AliasNode(sig, maxdepth, env=self.env)) + node.append(AliasNode(sig, aliasOptions, env=self.env)) contentnode = addnodes.desc_content() node.append(contentnode) From e46779ef1547a9cc522ce1f16c74012f6ca69eb1 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Fri, 12 Feb 2021 17:33:46 +0100 Subject: [PATCH 5/5] mypy fixes --- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 2030aab61f0..061010d6651 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3478,7 +3478,7 @@ def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool, if recurse: if skipThis: - childContainer = nodes + childContainer = nodes # type: Union[List[Node], addnodes.desc] else: content = addnodes.desc_content() desc = addnodes.desc() diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 9d94f651a11..7ee023d1dc1 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7089,7 +7089,7 @@ def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool, if recurse: if skipThis: - childContainer = nodes + childContainer = nodes # type: Union[List[Node], addnodes.desc] else: content = addnodes.desc_content() desc = addnodes.desc()