diff --git a/CHANGES b/CHANGES index fc85ceb21..576d8caca 100644 --- a/CHANGES +++ b/CHANGES @@ -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 ---------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index adee42c8c..3ac90b5fb 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -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 \ std::array + .. 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 diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7cf83fa7d..ff5487598 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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(), diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 6614802e5..fe5e37f23 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -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'})