Add 'cpp:struct' directive and 'cpp:struct' role.

They are cosmetic variants of the class directive/role.
This commit is contained in:
Frank Erens
2019-01-09 19:27:54 +01:00
committed by Jakob Lykke Andersen
parent 4e6dacd81b
commit 9e6fe21c6a
4 changed files with 55 additions and 41 deletions

View File

@@ -58,6 +58,8 @@ Incompatible changes
to insert custom user definitions. See :ref:`latex-macros`.
* quickstart: Simplify generated ``conf.py``
* websupport: unbundled from sphinx core. Please use sphinxcontrib-websupport
* C++, the visibility of base classes is now always rendered as present in the
input. That is, ``private`` is now shown, where it was ellided before.
Deprecated
----------
@@ -160,6 +162,7 @@ Features added
* #5841: apidoc: Add --extensions option to sphinx-apidoc
* #4981: C++, added an alias directive for inserting lists of declarations,
that references existing declarations (e.g., for making a synopsis).
* C++: add ``cpp:struct`` to complement ``cpp:class``.
Bugs fixed
----------

View File

@@ -548,11 +548,16 @@ The following directives are available. All declarations can start with a
visibility statement (``public``, ``private`` or ``protected``).
.. rst:directive:: .. cpp:class:: class specifier
.. cpp:struct:: class specifier
Describe a class/struct, possibly with specification of inheritance, e.g.,::
.. cpp:class:: MyClass : public MyBase, MyOtherBase
The difference between :rst:dir:`cpp:class` and :rst:dir:`cpp:struct` is
only cosmetic: the prefix rendered in the output, and the specifier shown
in the index.
The class can be directly declared inside a nested scope, e.g.,::
.. cpp:class:: OuterScope::MyClass : public MyBase, MyOtherBase
@@ -574,6 +579,9 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:class:: template<typename T> \
std::array<T, 42>
.. versionadded:: 2.0
The :rst:dir:`cpp:struct` directive.
.. rst:directive:: .. cpp:function:: (member) function prototype
Describe a function or member function, e.g.,::
@@ -1037,6 +1045,7 @@ These roles link to the given declaration types:
.. rst:role:: cpp:any
cpp:class
cpp:struct
cpp:func
cpp:member
cpp:var
@@ -1048,6 +1057,9 @@ These roles link to the given declaration types:
Reference a C++ declaration by name (see below for details). The name must
be properly qualified relative to the position of the link.
.. versionadded:: 2.0
The :rst:role:`cpp:struct` role as alias for the :rst:role:`cpp:class` role.
.. admonition:: Note on References with Templates Parameters/Arguments
These roles follow the Sphinx :ref:`xref-syntax` rules. This means care must be

View File

@@ -3471,7 +3471,8 @@ class ASTBaseClass(ASTBase):
def _stringify(self, transform):
# type: (Callable[[Any], str]) -> str
res = []
if self.visibility != 'private':
if self.visibility is not None:
res.append(self.visibility)
res.append(' ')
if self.virtual:
@@ -3484,7 +3485,7 @@ class ASTBaseClass(ASTBase):
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, str, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
if self.visibility != 'private':
if self.visibility is not None:
signode += addnodes.desc_annotation(self.visibility,
self.visibility)
signode += nodes.Text(' ')
@@ -3624,9 +3625,10 @@ class ASTEnumerator(ASTBase):
class ASTDeclaration(ASTBase):
def __init__(self, objectType, visibility, templatePrefix, declaration):
# type: (str, str, ASTTemplateDeclarationPrefix, Any) -> None
def __init__(self, objectType, directiveType, visibility, templatePrefix, declaration):
# type: (str, str, str, ASTTemplateDeclarationPrefix, Any) -> None
self.objectType = objectType
self.directiveType = directiveType
self.visibility = visibility
self.templatePrefix = templatePrefix
self.declaration = declaration
@@ -3634,8 +3636,6 @@ class ASTDeclaration(ASTBase):
self.symbol = None # type: Symbol
# set by CPPObject._add_enumerator_to_parent
self.enumeratorScopedSymbol = None # type: Symbol
# set by CPPEnumObject.parse_definition
self.scoped = None # type: str
def clone(self):
# type: () -> ASTDeclaration
@@ -3643,8 +3643,8 @@ class ASTDeclaration(ASTBase):
templatePrefixClone = self.templatePrefix.clone()
else:
templatePrefixClone = None
return ASTDeclaration(self.objectType, self.visibility,
templatePrefixClone,
return ASTDeclaration(self.objectType, self.directiveType,
self.visibility, templatePrefixClone,
self.declaration.clone())
@property
@@ -3725,14 +3725,20 @@ class ASTDeclaration(ASTBase):
elif self.objectType == 'function':
pass
elif self.objectType == 'class':
mainDeclNode += addnodes.desc_annotation('class ', 'class ')
assert self.directiveType in ('class', 'struct')
prefix = self.directiveType + ' '
mainDeclNode += addnodes.desc_annotation(prefix, prefix)
elif self.objectType == 'union':
mainDeclNode += addnodes.desc_annotation('union ', 'union ')
elif self.objectType == 'enum':
prefix = 'enum '
if self.scoped:
prefix += self.scoped
prefix += ' '
if self.directiveType == 'enum':
prefix = 'enum '
elif self.directiveType == 'enum-class':
prefix = 'enum class '
elif self.directiveType == 'enum-struct':
prefix = 'enum struct '
else:
assert False # wrong directiveType used
mainDeclNode += addnodes.desc_annotation(prefix, prefix)
elif self.objectType == 'enumerator':
mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
@@ -3844,7 +3850,7 @@ class Symbol:
continue
# only add a declaration if we our self are from a declaration
if self.declaration:
decl = ASTDeclaration('templateParam', None, None, p)
decl = ASTDeclaration('templateParam', None, None, None, p)
else:
decl = None
nne = ASTNestedNameElement(p.get_identifier(), None)
@@ -3859,7 +3865,7 @@ class Symbol:
if nn is None:
continue
# (comparing to the template params: we have checked that we are a declaration)
decl = ASTDeclaration('functionParam', None, None, p)
decl = ASTDeclaration('functionParam', None, None, None, p)
assert not nn.rooted
assert len(nn.names) == 1
self._add_symbols(nn, [], decl, self.docname)
@@ -5997,7 +6003,7 @@ class DefinitionParser:
if self.skip_string(':'):
while 1:
self.skip_ws()
visibility = 'private'
visibility = None
virtual = False
pack = False
if self.skip_word_and_ws('virtual'):
@@ -6231,11 +6237,15 @@ class DefinitionParser:
templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
return templatePrefix
def parse_declaration(self, objectType):
# type: (str) -> ASTDeclaration
if objectType not in ('type', 'concept', 'member',
'function', 'class', 'union', 'enum', 'enumerator'):
def parse_declaration(self, objectType, directiveType):
# type: (str, str) -> ASTDeclaration
if objectType not in ('class', 'union', 'function', 'member', 'type',
'concept', 'enum', 'enumerator'):
raise Exception('Internal error, unknown objectType "%s".' % objectType)
if directiveType not in ('class', 'struct', 'union', 'function', 'member', 'var',
'type', 'concept',
'enum', 'enum-struct', 'enum-class', 'enumerator'):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
visibility = None
templatePrefix = None
declaration = None # type: Any
@@ -6285,7 +6295,7 @@ class DefinitionParser:
templatePrefix,
fullSpecShorthand=False,
isMember=objectType == 'member')
return ASTDeclaration(objectType, visibility,
return ASTDeclaration(objectType, directiveType, visibility,
templatePrefix, declaration)
def parse_namespace_object(self):
@@ -6315,7 +6325,7 @@ class DefinitionParser:
except DefinitionError as e1:
try:
self.pos = pos
res2 = self.parse_declaration('function')
res2 = self.parse_declaration('function', 'function')
# if there are '()' left, just skip them
self.skip_ws()
self.skip_string('()')
@@ -6405,7 +6415,7 @@ class CPPObject(ObjectDescription):
# TODO: maybe issue a warning, enumerators in non-enums is weird,
# but it is somewhat equivalent to unscoped enums, without the enum
return
if parentDecl.scoped:
if parentDecl.directiveType != 'enum':
return
targetSymbol = parentSymbol.parent
@@ -6496,7 +6506,7 @@ class CPPObject(ObjectDescription):
def parse_definition(self, parser):
# type: (DefinitionParser) -> ASTDeclaration
return parser.parse_declaration(self.object_type)
return parser.parse_declaration(self.object_type, self.objtype)
def describe_signature(self, signode, ast, options):
# type: (addnodes.desc_signature, Any, Dict) -> None
@@ -6615,20 +6625,6 @@ class CPPUnionObject(CPPObject):
class CPPEnumObject(CPPObject):
object_type = 'enum'
def parse_definition(self, parser):
# type: (DefinitionParser) -> ASTDeclaration
ast = super().parse_definition(parser)
# self.objtype is set by ObjectDescription in run()
if self.objtype == "enum":
ast.scoped = None
elif self.objtype == "enum-struct":
ast.scoped = "struct"
elif self.objtype == "enum-class":
ast.scoped = "class"
else:
assert False
return ast
class CPPEnumeratorObject(CPPObject):
object_type = 'enumerator'
@@ -6968,6 +6964,7 @@ class CPPDomain(Domain):
directives = {
# declarations
'class': CPPClassObject,
'struct': CPPClassObject,
'union': CPPUnionObject,
'function': CPPFunctionObject,
'member': CPPMemberObject,
@@ -6988,6 +6985,7 @@ class CPPDomain(Domain):
roles = {
'any': CPPXRefRole(),
'class': CPPXRefRole(),
'struct': CPPXRefRole(),
'union': CPPXRefRole(),
'func': CPPXRefRole(fix_parens=True),
'member': CPPXRefRole(),

View File

@@ -25,7 +25,7 @@ def parse(name, string):
cpp_paren_attributes = ["paren_attr"]
parser = DefinitionParser(string, None, Config())
parser.allowFallbackExpressionParsing = False
ast = parser.parse_declaration(name)
ast = parser.parse_declaration(name, name)
parser.assert_end()
# The scopedness would usually have been set by CPPEnumObject
if name == "enum":
@@ -526,11 +526,12 @@ def test_class_definitions():
check('class', 'A', {1: "A", 2: "1A"})
check('class', 'A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
check('class', 'A : B', {1: "A", 2: "1A"})
check('class', 'A : private B', {1: "A", 2: "1A"}, output='A : B')
check('class', 'A : private B', {1: "A", 2: "1A"})
check('class', 'A : public B', {1: "A", 2: "1A"})
check('class', 'A : B, C', {1: "A", 2: "1A"})
check('class', 'A : B, protected C, D', {1: "A", 2: "1A"})
check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='A : virtual B')
check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='A : private virtual B')
check('class', 'A : private virtual B', {1: 'A', 2: '1A'})
check('class', 'A : B, virtual C', {1: 'A', 2: '1A'})
check('class', 'A : public virtual B', {1: 'A', 2: '1A'})
check('class', 'A : B, C...', {1: 'A', 2: '1A'})