diff --git a/CHANGES b/CHANGES index 758fb80e0..3bc99f51b 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 279d0f401..1a8affe56 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 c4ebdaf37..2030aab61 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3435,12 +3435,12 @@ class CNamespacePopObject(SphinxDirective): 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 @@ class AliasNode(nodes.Element): 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 @@ class AliasTransform(SphinxTransform): 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 @@ class AliasTransform(SphinxTransform): 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 @@ class AliasTransform(SphinxTransform): 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 @@ class CAliasObject(ObjectDescription): 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]