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. * C++, also hyperlink operator overloads in expressions and alias declarations.
* #8247: Allow production lists to refer to tokens from other production groups * #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 * #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 Bugs fixed
---------- ----------
@ -148,6 +154,9 @@ Bugs fixed
builds builds
* #8865: LaTeX: Restructure the index nodes inside title nodes only on LaTeX * #8865: LaTeX: Restructure the index nodes inside title nodes only on LaTeX
builds 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 Testing
-------- --------

View File

@ -755,6 +755,13 @@ The following directive can be used for this purpose.
.. versionadded:: 3.3 .. 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:: .. c:namespace-pop::
@ -1179,6 +1186,24 @@ The following directive can be used for this purpose.
.. versionadded:: 2.0 .. 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 Constrained Templates
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~

View File

@ -3435,12 +3435,12 @@ class CNamespacePopObject(SphinxDirective):
class AliasNode(nodes.Element): 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: parentKey: LookupKey = None) -> None:
super().__init__() super().__init__()
self.sig = sig self.sig = sig
self.maxdepth = maxdepth self.aliasOptions = aliasOptions
assert maxdepth >= 0
self.document = document self.document = document
if env is not None: if env is not None:
if 'c:parent_symbol' not in env.temp_data: if 'c:parent_symbol' not in env.temp_data:
@ -3452,19 +3452,16 @@ class AliasNode(nodes.Element):
self.parentKey = parentKey self.parentKey = parentKey
def copy(self) -> 'AliasNode': 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) env=None, parentKey=self.parentKey)
class AliasTransform(SphinxTransform): class AliasTransform(SphinxTransform):
default_priority = ReferencesResolver.default_priority - 1 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, skipThis: bool,
nodes = [] # type: List[Node] aliasOptions: dict, renderOptions: dict,
options = dict() # type: ignore document: Any) -> List[Node]:
signode = addnodes.desc_signature('', '')
nodes.append(signode)
s.declaration.describe_signature(signode, 'markName', self.env, options)
if maxdepth == 0: if maxdepth == 0:
recurse = True recurse = True
elif maxdepth == 1: elif maxdepth == 1:
@ -3472,26 +3469,43 @@ class AliasTransform(SphinxTransform):
else: else:
maxdepth -= 1 maxdepth -= 1
recurse = True 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 recurse:
content = addnodes.desc_content() if skipThis:
desc = addnodes.desc() childContainer = nodes # type: Union[List[Node], addnodes.desc]
content.append(desc) else:
desc.document = document content = addnodes.desc_content()
desc['domain'] = 'c' desc = addnodes.desc()
# 'desctype' is a backwards compatible attribute content.append(desc)
desc['objtype'] = desc['desctype'] = 'alias' desc.document = document
desc['noindex'] = True desc['domain'] = 'c'
# 'desctype' is a backwards compatible attribute
desc['objtype'] = desc['desctype'] = 'alias'
desc['noindex'] = True
childContainer = desc
for sChild in s.children: for sChild in s.children:
childNodes = self._render_symbol(sChild, maxdepth, document) if sChild.declaration is None:
desc.extend(childNodes) 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) nodes.append(content)
return nodes return nodes
def apply(self, **kwargs: Any) -> None: def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(AliasNode): for node in self.document.traverse(AliasNode):
node = cast(AliasNode, node)
sig = node.sig sig = node.sig
parentKey = node.parentKey parentKey = node.parentKey
try: try:
@ -3531,17 +3545,40 @@ class AliasTransform(SphinxTransform):
location=node) location=node)
node.replace_self(signode) node.replace_self(signode)
continue 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) node.replace_self(nodes)
class CAliasObject(ObjectDescription): class CAliasObject(ObjectDescription):
option_spec = { option_spec = {
'maxdepth': directives.nonnegative_int 'maxdepth': directives.nonnegative_int,
'noroot': directives.flag,
} # type: Dict } # type: Dict
def run(self) -> List[Node]: 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: if ':' in self.name:
self.domain, self.objtype = self.name.split(':', 1) self.domain, self.objtype = self.name.split(':', 1)
else: else:
@ -3555,10 +3592,19 @@ class CAliasObject(ObjectDescription):
node['noindex'] = True node['noindex'] = True
self.names = [] # type: List[str] 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() signatures = self.get_signatures()
for i, sig in enumerate(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] return [node]

View File

@ -10,7 +10,7 @@
import re import re
from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type, from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type,
TypeVar, Union) TypeVar, Union, cast)
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, TextElement, system_message from docutils.nodes import Element, Node, TextElement, system_message
@ -3742,6 +3742,7 @@ class ASTDeclaration(ASTBase):
elif self.objectType == 'enumerator': elif self.objectType == 'enumerator':
mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
else: else:
print(self.objectType)
assert False assert False
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
lastDeclNode = mainDeclNode lastDeclNode = mainDeclNode
@ -7046,10 +7047,12 @@ class CPPNamespacePopObject(SphinxDirective):
class AliasNode(nodes.Element): 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: parentKey: LookupKey = None) -> None:
super().__init__() super().__init__()
self.sig = sig self.sig = sig
self.aliasOptions = aliasOptions
if env is not None: if env is not None:
if 'cpp:parent_symbol' not in env.temp_data: if 'cpp:parent_symbol' not in env.temp_data:
root = env.domaindata['cpp']['root_symbol'] root = env.domaindata['cpp']['root_symbol']
@ -7060,14 +7063,62 @@ class AliasNode(nodes.Element):
self.parentKey = parentKey self.parentKey = parentKey
def copy(self) -> 'AliasNode': 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): class AliasTransform(SphinxTransform):
default_priority = ReferencesResolver.default_priority - 1 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: def apply(self, **kwargs: Any) -> None:
for node in self.document.traverse(AliasNode): for node in self.document.traverse(AliasNode):
node = cast(AliasNode, node)
sig = node.sig sig = node.sig
parentKey = node.parentKey parentKey = node.parentKey
try: try:
@ -7131,22 +7182,31 @@ class AliasTransform(SphinxTransform):
signode.clear() signode.clear()
signode += addnodes.desc_name(sig, sig) 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) location=node)
node.replace_self(signode) node.replace_self(signode)
else: else:
nodes = [] nodes = []
options = dict() renderOptions = {
options['tparam-line-spec'] = False 'tparam-line-spec': False,
}
for s in symbols: for s in symbols:
signode = addnodes.desc_signature(sig, '') assert s.declaration is not None
nodes.append(signode) res = self._render_symbol(
s.declaration.describe_signature(signode, 'markName', self.env, options) s, maxdepth=node.aliasOptions['maxdepth'],
skipThis=node.aliasOptions['noroot'],
aliasOptions=node.aliasOptions,
renderOptions=renderOptions,
document=node.document)
nodes.extend(res)
node.replace_self(nodes) node.replace_self(nodes)
class CPPAliasObject(ObjectDescription): class CPPAliasObject(ObjectDescription):
option_spec = {} # type: Dict option_spec = {
'maxdepth': directives.nonnegative_int,
'noroot': directives.flag,
} # type: Dict
def run(self) -> List[Node]: def run(self) -> List[Node]:
""" """
@ -7166,9 +7226,19 @@ class CPPAliasObject(ObjectDescription):
node['objtype'] = node['desctype'] = self.objtype node['objtype'] = node['desctype'] = self.objtype
self.names = [] # type: List[str] 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() signatures = self.get_signatures()
for i, sig in enumerate(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() contentnode = addnodes.desc_content()
node.append(contentnode) node.append(contentnode)