mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
C++, initial enum support (see #772)
This commit is contained in:
parent
90c7adba6d
commit
c010b47589
4
CHANGES
4
CHANGES
@ -187,7 +187,7 @@ Bugs fixed
|
||||
Thanks to Takeshi Komiya.
|
||||
* PR#265: Fix could not capture caption of graphviz node by xref. Thanks to
|
||||
Takeshi Komiya.
|
||||
* PR#263, #1013, #1103: Rewrite of C++ domain. Thanks to Jakob Lykke Andersen.
|
||||
* PR#263, #1013, #1103, #772: Rewrite of C++ domain. Thanks to Jakob Lykke Andersen.
|
||||
|
||||
* Hyperlinks to all found nested names and template arguments (#1103).
|
||||
* Support for function types everywhere, e.g., in
|
||||
@ -203,6 +203,8 @@ Bugs fixed
|
||||
* Index names may be different now. Elements are indexed by their fully
|
||||
qualified name. It should be rather easy to change this behaviour and
|
||||
potentially index by namespaces/classes as well.
|
||||
* Support for scoped and unscoped enums. Enumerators in unscoped enums
|
||||
are injected into the parent scope in addition to the enum scope.
|
||||
|
||||
* PR#258, #939: Add dedent option for :rst:dir:`code-block` and
|
||||
:rst:dir:`literal-include`. Thanks to Zafar Siddiqui.
|
||||
|
@ -520,8 +520,8 @@ The C++ Domain
|
||||
|
||||
The C++ domain (name **cpp**) supports documenting C++ projects.
|
||||
|
||||
The following directives are available. All declarations can start with a visibility statement
|
||||
(``public``, ``private`` or ``protected``).
|
||||
The following directives are available. All declarations except for enumerators
|
||||
can start with a visibility statement (``public``, ``private`` or ``protected``).
|
||||
|
||||
.. rst:directive:: .. cpp:class:: class speicifer
|
||||
|
||||
@ -578,6 +578,35 @@ The following directives are available. All declarations can start with a visibi
|
||||
|
||||
Declaration of a type alias with unspecified type.
|
||||
|
||||
.. rst:directive:: .. cpp:enum:: enum declaration
|
||||
|
||||
Describe a (scoped) enum, possibly with the underlying type specified. Note that for scoped
|
||||
enums the ``struct``/``class`` keyword must come before the optional visibility specifier.
|
||||
Any enumerators declared inside an unscoped enum will be declared both in the enum scope
|
||||
and in the parent scope.
|
||||
Examples:
|
||||
|
||||
.. cpp:enum:: MyEnum
|
||||
|
||||
An unscoped enum.
|
||||
|
||||
.. cpp:enum:: MySpecificEnum : long
|
||||
|
||||
An unscoped enum with specified underlying type.
|
||||
|
||||
.. cpp:enum:: class MyScopedEnum
|
||||
|
||||
A scoped enum.
|
||||
|
||||
.. cpp:enum:: struct protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type
|
||||
|
||||
A scoped enum with non-default visibility, and with a specified underlying type.
|
||||
|
||||
.. rst:directive:: .. cpp:enumerator:: name
|
||||
.. cpp:enumerator:: name = constant
|
||||
|
||||
Describe an enumerator, optionally with its value defined.
|
||||
|
||||
.. rst:directive:: .. cpp:namespace:: namespace
|
||||
|
||||
Select the current namespace for the following objects. Note that the namespace
|
||||
|
@ -108,7 +108,15 @@
|
||||
memberFunctionInit -> "=" "0"
|
||||
# (note: only "0" is allowed as the value, according to the standard,
|
||||
# right?)
|
||||
|
||||
|
||||
enum-head ->
|
||||
enum-key attribute-specifier-seq[opt] nested-name-specifier[opt] identifier enum-base[opt]
|
||||
enum-key -> "enum" | "enum struct" | "enum class"
|
||||
enum-base ->
|
||||
":" type
|
||||
enumerator-definition ->
|
||||
identifier
|
||||
| identifier "=" constant-expression
|
||||
|
||||
We additionally add the possibility for specifying the visibility as the
|
||||
first thing.
|
||||
@ -127,7 +135,6 @@
|
||||
grammar, typedef-like: no initilizer
|
||||
decl-specifier-seq declerator
|
||||
|
||||
|
||||
member_object:
|
||||
goal: as a type_object which must have a declerator, and optionally
|
||||
with a initializer
|
||||
@ -138,6 +145,26 @@
|
||||
goal: a function declaration, TODO: what about templates? for now: skip
|
||||
grammar: no initializer
|
||||
decl-specifier-seq declerator
|
||||
|
||||
class_object:
|
||||
goal: a class declaration, but with specification of a base class, TODO: what about templates? for now: skip
|
||||
grammar:
|
||||
nested-name
|
||||
| nested-name ":" 'comma-separated list of nested-name optionally with visibility'
|
||||
|
||||
enum_object:
|
||||
goal: an unscoped enum or a scoped enum, optionally with the underlying type specified
|
||||
grammar:
|
||||
("class" | "struct")[opt] visibility[opt] nested-name (":" type)[opt]
|
||||
enumerator_object:
|
||||
goal: an element in a scoped or unscoped enum. The name should be injected according to the scopedness.
|
||||
grammar:
|
||||
nested-name ("=" constant-expression)
|
||||
|
||||
namespace_object:
|
||||
goal: a directive to put all following declarations in a specific scope
|
||||
grammar:
|
||||
nested-name
|
||||
"""
|
||||
|
||||
import re
|
||||
@ -1003,12 +1030,13 @@ class ASTBaseClass(ASTBase):
|
||||
signode += addnodes.desc_annotation(
|
||||
self.visibility, self.visibility)
|
||||
signode += nodes.Text(' ')
|
||||
self.name.describe_signature(signode, mode, env)
|
||||
self.name.describe_signature(signode, mode, env)
|
||||
|
||||
|
||||
class ASTClass(ASTBase):
|
||||
def __init__(self, name, bases):
|
||||
def __init__(self, name, visibility, bases):
|
||||
self.name = name
|
||||
self.visibility = visibility
|
||||
self.bases = bases
|
||||
|
||||
def get_id(self):
|
||||
@ -1016,6 +1044,9 @@ class ASTClass(ASTBase):
|
||||
|
||||
def __unicode__(self):
|
||||
res = []
|
||||
if self.visibility != 'public':
|
||||
res.append(self.visibility)
|
||||
res.append(' ')
|
||||
res.append(text_type(self.name))
|
||||
if len(self.bases) > 0:
|
||||
res.append(' : ')
|
||||
@ -1029,6 +1060,10 @@ class ASTClass(ASTBase):
|
||||
|
||||
def describe_signature(self, signode, mode, env):
|
||||
_verify_description_mode(mode)
|
||||
if self.visibility != 'public':
|
||||
signode += addnodes.desc_annotation(
|
||||
self.visibility, self.visibility)
|
||||
signode += nodes.Text(' ')
|
||||
self.name.describe_signature(signode, mode, env)
|
||||
if len(self.bases) > 0:
|
||||
signode += nodes.Text(' : ')
|
||||
@ -1037,6 +1072,63 @@ class ASTClass(ASTBase):
|
||||
signode += nodes.Text(', ')
|
||||
signode.pop()
|
||||
|
||||
class ASTEnum(ASTBase):
|
||||
def __init__(self, name, visibility, scoped, underlyingType):
|
||||
self.name = name
|
||||
self.visibility = visibility
|
||||
self.scoped = scoped
|
||||
self.underlyingType = underlyingType
|
||||
|
||||
def get_id(self):
|
||||
return _id_prefix + self.prefixedName.get_id()
|
||||
|
||||
def __unicode__(self):
|
||||
res = []
|
||||
if self.scoped:
|
||||
res.append(self.scoped)
|
||||
res.append(' ')
|
||||
if self.visibility != 'public':
|
||||
res.append(self.visibility)
|
||||
res.append(' ')
|
||||
res.append(text_type(self.name))
|
||||
if self.underlyingType:
|
||||
res.append(' : ')
|
||||
res.append(text_type(self.underlyingType))
|
||||
return u''.join(res)
|
||||
|
||||
def describe_signature(self, signode, mode, env):
|
||||
_verify_description_mode(mode)
|
||||
# self.scoped has been done by the CPPEnumObject
|
||||
if self.visibility != 'public':
|
||||
signode += addnodes.desc_annotation(
|
||||
self.visibility, self.visibility)
|
||||
signode += nodes.Text(' ')
|
||||
self.name.describe_signature(signode, mode, env)
|
||||
if self.underlyingType:
|
||||
signode += nodes.Text(' : ')
|
||||
self.underlyingType.describe_signature(signode, 'noneIsName', env)
|
||||
|
||||
class ASTEnumerator(ASTBase):
|
||||
def __init__(self, name, init):
|
||||
self.name = name
|
||||
self.init = init
|
||||
|
||||
def get_id(self):
|
||||
return _id_prefix + self.prefixedName.get_id()
|
||||
|
||||
def __unicode__(self):
|
||||
res = []
|
||||
res.append(text_type(self.name))
|
||||
if self.init:
|
||||
res.append(text_type(self.init))
|
||||
return u''.join(res)
|
||||
|
||||
def describe_signature(self, signode, mode, env):
|
||||
_verify_description_mode(mode)
|
||||
self.name.describe_signature(signode, mode, env)
|
||||
if self.init:
|
||||
self.init.describe_signature(signode, 'noneIsName')
|
||||
|
||||
|
||||
class DefinitionParser(object):
|
||||
# those without signedness and size modifiers
|
||||
@ -1583,6 +1675,9 @@ class DefinitionParser(object):
|
||||
return ASTTypeWithInit(type, init)
|
||||
|
||||
def _parse_class(self):
|
||||
classVisibility = 'public'
|
||||
if self.match(_visibility_re):
|
||||
classVisibility = self.matched_text
|
||||
name = self._parse_nested_name()
|
||||
bases = []
|
||||
self.skip_ws()
|
||||
@ -1599,8 +1694,35 @@ class DefinitionParser(object):
|
||||
continue
|
||||
else:
|
||||
break
|
||||
return ASTClass(name, bases)
|
||||
|
||||
return ASTClass(name, classVisibility, bases)
|
||||
|
||||
def _parse_enum(self):
|
||||
scoped = None
|
||||
self.skip_ws()
|
||||
if self.skip_word_and_ws('class'):
|
||||
scoped = 'class'
|
||||
elif self.skip_word_and_ws('struct'):
|
||||
scoped = 'struct'
|
||||
visibility = 'public'
|
||||
if self.match(_visibility_re):
|
||||
visibility = self.matched_text
|
||||
self.skip_ws()
|
||||
name = self._parse_nested_name()
|
||||
self.skip_ws()
|
||||
underlyingType = None
|
||||
if self.skip_string(':'):
|
||||
underlyingType = self._parse_type()
|
||||
return ASTEnum(name, visibility, scoped, underlyingType)
|
||||
|
||||
def _parse_enumerator(self):
|
||||
name = self._parse_nested_name()
|
||||
self.skip_ws()
|
||||
init = None
|
||||
if self.skip_string('='):
|
||||
self.skip_ws()
|
||||
init = ASTInitializer(self.read_rest())
|
||||
return ASTEnumerator(name, init)
|
||||
|
||||
def parse_type_object(self):
|
||||
res = self._parse_type(outer='type')
|
||||
res.objectType = 'type'
|
||||
@ -1620,6 +1742,16 @@ class DefinitionParser(object):
|
||||
res = self._parse_class()
|
||||
res.objectType = 'class'
|
||||
return res
|
||||
|
||||
def parse_enum_object(self):
|
||||
res = self._parse_enum()
|
||||
res.objectType = 'enum'
|
||||
return res
|
||||
|
||||
def parse_enumerator_object(self):
|
||||
res = self._parse_enumerator()
|
||||
res.objectType = 'enumerator'
|
||||
return res
|
||||
|
||||
def parse_namespace_object(self):
|
||||
res = self._parse_nested_name()
|
||||
@ -1658,14 +1790,27 @@ class CPPObject(ObjectDescription):
|
||||
signode['first'] = (not self.names)
|
||||
self.state.document.note_explicit_target(signode)
|
||||
if not name in objects:
|
||||
objects.setdefault(name,
|
||||
(self.env.docname, ast.objectType, theid))
|
||||
objects.setdefault(name, (self.env.docname, ast))
|
||||
if ast.objectType == 'enumerator':
|
||||
# find the parent, if it exists && is an enum && it's unscoped, then add the name to the parent scope
|
||||
assert len(ast.prefixedName.names) > 0
|
||||
parentPrefixedAstName = ASTNestedName(ast.prefixedName.names[:-1])
|
||||
parentPrefixedName = text_type(parentPrefixedAstName)
|
||||
if parentPrefixedName in objects:
|
||||
docname, parentAst = objects[parentPrefixedName]
|
||||
if parentAst.objectType == 'enum' and not parentAst.scoped:
|
||||
enumeratorName = ASTNestedName([ast.prefixedName.names[-1]])
|
||||
assert len(parentAst.prefixedName.names) > 0
|
||||
enumScope = ASTNestedName(parentAst.prefixedName.names[:-1])
|
||||
unscopedName = enumeratorName.prefix_nested_name(enumScope)
|
||||
txtUnscopedName = text_type(unscopedName)
|
||||
if not txtUnscopedName in objects:
|
||||
objects.setdefault(txtUnscopedName, (self.env.docname, ast))
|
||||
# add the uninstantiated template if it doesn't exist
|
||||
uninstantiated = ast.prefixedName.get_name_no_last_template()
|
||||
if uninstantiated != name and uninstantiated not in objects:
|
||||
signode['names'].append(uninstantiated)
|
||||
objects.setdefault(uninstantiated, (
|
||||
self.env.docname, ast.objectType, theid))
|
||||
objects.setdefault(uninstantiated, (self.env.docname, ast))
|
||||
self.env.temp_data['cpp:lastname'] = ast.prefixedName
|
||||
|
||||
indextext = self.get_index_text(name)
|
||||
@ -1757,6 +1902,45 @@ class CPPClassObject(CPPObject):
|
||||
def describe_signature(self, signode, ast):
|
||||
signode += addnodes.desc_annotation('class ', 'class ')
|
||||
ast.describe_signature(signode, 'lastIsName', self.env)
|
||||
|
||||
|
||||
class CPPEnumObject(CPPObject):
|
||||
def get_index_text(self, name):
|
||||
return _('%s (C++ enum)') % name
|
||||
|
||||
def before_content(self):
|
||||
lastname = self.env.temp_data['cpp:lastname']
|
||||
assert lastname
|
||||
if 'cpp:parent' in self.env.temp_data:
|
||||
self.env.temp_data['cpp:parent'].append(lastname)
|
||||
else:
|
||||
self.env.temp_data['cpp:parent'] = [lastname]
|
||||
|
||||
def after_content(self):
|
||||
self.env.temp_data['cpp:parent'].pop()
|
||||
|
||||
def parse_definition(self, parser):
|
||||
return parser.parse_enum_object()
|
||||
|
||||
def describe_signature(self, signode, ast):
|
||||
prefix = 'enum '
|
||||
if ast.scoped:
|
||||
prefix += ast.scoped
|
||||
prefix += ' '
|
||||
signode += addnodes.desc_annotation(prefix, prefix)
|
||||
ast.describe_signature(signode, 'lastIsName', self.env)
|
||||
|
||||
|
||||
class CPPEnumeratorObject(CPPObject):
|
||||
def get_index_text(self, name):
|
||||
return _('%s (C++ enumerator)') % name
|
||||
|
||||
def parse_definition(self, parser):
|
||||
return parser.parse_enumerator_object()
|
||||
|
||||
def describe_signature(self, signode, ast):
|
||||
signode += addnodes.desc_annotation('enumerator ', 'enumerator ')
|
||||
ast.describe_signature(signode, 'lastIsName', self.env)
|
||||
|
||||
|
||||
class CPPNamespaceObject(Directive):
|
||||
@ -1804,7 +1988,6 @@ class CPPXRefRole(XRefRole):
|
||||
title = title[dcolon + 2:]
|
||||
return title, target
|
||||
|
||||
|
||||
class CPPDomain(Domain):
|
||||
"""C++ language domain."""
|
||||
name = 'cpp'
|
||||
@ -1813,7 +1996,9 @@ class CPPDomain(Domain):
|
||||
'class': ObjType(l_('class'), 'class'),
|
||||
'function': ObjType(l_('function'), 'func'),
|
||||
'member': ObjType(l_('member'), 'member'),
|
||||
'type': ObjType(l_('type'), 'type')
|
||||
'type': ObjType(l_('type'), 'type'),
|
||||
'enum': ObjType(l_('enum'), 'enum'),
|
||||
'enumerator': ObjType(l_('enumerator'), 'enumerator')
|
||||
}
|
||||
|
||||
directives = {
|
||||
@ -1821,16 +2006,20 @@ class CPPDomain(Domain):
|
||||
'function': CPPFunctionObject,
|
||||
'member': CPPMemberObject,
|
||||
'type': CPPTypeObject,
|
||||
'enum': CPPEnumObject,
|
||||
'enumerator': CPPEnumeratorObject,
|
||||
'namespace': CPPNamespaceObject
|
||||
}
|
||||
roles = {
|
||||
'class': CPPXRefRole(),
|
||||
'func': CPPXRefRole(fix_parens=True),
|
||||
'member': CPPXRefRole(),
|
||||
'type': CPPXRefRole()
|
||||
'type': CPPXRefRole(),
|
||||
'enum': CPPXRefRole(),
|
||||
'enumerator': CPPXRefRole()
|
||||
}
|
||||
initial_data = {
|
||||
'objects': {}, # prefixedName -> (docname, objectType, id)
|
||||
'objects': {}, # prefixedName -> (docname, ast)
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
@ -1847,8 +2036,8 @@ class CPPDomain(Domain):
|
||||
name = nameAst.get_name_no_last_template()
|
||||
if name not in self.data['objects']:
|
||||
return None
|
||||
docname, objectType, id = self.data['objects'][name]
|
||||
return make_refnode(builder, fromdocname, docname, id, contnode,
|
||||
docname, ast = self.data['objects'][name]
|
||||
return make_refnode(builder, fromdocname, docname, ast.get_id(), contnode,
|
||||
name)
|
||||
|
||||
parser = DefinitionParser(target)
|
||||
@ -1874,5 +2063,5 @@ class CPPDomain(Domain):
|
||||
return None
|
||||
|
||||
def get_objects(self):
|
||||
for refname, (docname, type, theid) in iteritems(self.data['objects']):
|
||||
yield (refname, refname, type, docname, refname, 1)
|
||||
for refname, (docname, ast) in iteritems(self.data['objects']):
|
||||
yield (refname, refname, ast.objectType, docname, refname, 1)
|
@ -108,6 +108,21 @@ def test_type_definitions():
|
||||
raises(DefinitionError, parse, 'function', 'int foo(D d=x(a')
|
||||
check('function', 'int foo(const A&... a)')
|
||||
check('function', 'virtual void f()')
|
||||
|
||||
check('class', 'public A', 'A')
|
||||
check('class', 'private A')
|
||||
|
||||
check('enum', 'A')
|
||||
check('enum', 'A : std::underlying_type<B>::type')
|
||||
check('enum', 'struct A')
|
||||
check('enum', 'struct A : unsigned int')
|
||||
check('enum', 'class A')
|
||||
check('enum', 'class A : unsigned int')
|
||||
check('enum', 'class public A', 'class A')
|
||||
check('enum', 'class private A')
|
||||
|
||||
check('enumerator', 'A')
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()')
|
||||
|
||||
def test_bases():
|
||||
check('class', 'A')
|
||||
|
Loading…
Reference in New Issue
Block a user