Merge pull request #8876 from jakobandersen/c_cpp_alias

C and C++, alias fixes and improvements
This commit is contained in:
Jakob Lykke Andersen 2021-02-12 17:46:44 +01:00 committed by GitHub
commit be6391fa0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 36 deletions

View File

@ -76,6 +76,12 @@ 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.
* 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
----------
@ -148,6 +154,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
--------

View File

@ -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::
@ -1179,6 +1186,24 @@ 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
.. 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
~~~~~~~~~~~~~~~~~~~~~

View File

@ -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, 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)
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,7 +3469,17 @@ 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:
if skipThis:
childContainer = nodes # type: Union[List[Node], addnodes.desc]
else:
content = addnodes.desc_content()
desc = addnodes.desc()
content.append(desc)
@ -3481,17 +3488,24 @@ class AliasTransform(SphinxTransform):
# 'desctype' is a backwards compatible attribute
desc['objtype'] = desc['desctype'] = 'alias'
desc['noindex'] = True
childContainer = desc
for sChild in s.children:
childNodes = self._render_symbol(sChild, maxdepth, document)
desc.extend(childNodes)
if sChild.declaration is None:
continue
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:
@ -3531,17 +3545,40 @@ class AliasTransform(SphinxTransform):
location=node)
node.replace_self(signode)
continue
# 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()
signode += addnodes.desc_name(sig, sig)
nodes = self._render_symbol(s, maxdepth=node.maxdepth, document=node.document)
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.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:
@ -3555,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]

View File

@ -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
@ -3742,6 +3742,7 @@ class ASTDeclaration(ASTBase):
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 @@ class CPPNamespacePopObject(SphinxDirective):
class AliasNode(nodes.Element):
def __init__(self, sig: str, env: "BuildEnvironment" = None,
def __init__(self, sig: str, aliasOptions: dict,
env: "BuildEnvironment" = None,
parentKey: LookupKey = None) -> None:
super().__init__()
self.sig = sig
self.aliasOptions = aliasOptions
if env is not None:
if 'cpp:parent_symbol' not in env.temp_data:
root = env.domaindata['cpp']['root_symbol']
@ -7060,14 +7063,62 @@ class AliasNode(nodes.Element):
self.parentKey = parentKey
def copy(self) -> 'AliasNode':
return self.__class__(self.sig, 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, skipThis: bool,
aliasOptions: dict, renderOptions: dict,
document: Any) -> List[Node]:
if maxdepth == 0:
recurse = True
elif maxdepth == 1:
recurse = False
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:
if skipThis:
childContainer = nodes # type: Union[List[Node], addnodes.desc]
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=maxdepth, skipThis=False,
aliasOptions=aliasOptions, renderOptions=renderOptions,
document=document)
childContainer.extend(childNodes)
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:
@ -7131,22 +7182,31 @@ class AliasTransform(SphinxTransform):
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:
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.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 = {} # type: Dict
option_spec = {
'maxdepth': directives.nonnegative_int,
'noroot': directives.flag,
} # type: Dict
def run(self) -> List[Node]:
"""
@ -7166,9 +7226,19 @@ class CPPAliasObject(ObjectDescription):
node['objtype'] = node['desctype'] = self.objtype
self.names = [] # type: List[str]
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, env=self.env))
node.append(AliasNode(sig, aliasOptions, env=self.env))
contentnode = addnodes.desc_content()
node.append(contentnode)