diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4f336a4bfb4..f5c54ed635d 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -267,7 +267,8 @@ class_object: goal: a class declaration, but with specification of a base class grammar: - nested-name "final"[opt] (":" base-specifier-list)[opt] + attribute-specifier-seq[opt] + nested-name "final"[opt] (":" base-specifier-list)[opt] base-specifier-list -> base-specifier "..."[opt] | base-specifier-list, base-specifier "..."[opt] @@ -281,7 +282,8 @@ goal: an unscoped enum or a scoped enum, optionally with the underlying type specified grammar: - ("class" | "struct")[opt] visibility[opt] nested-name (":" type)[opt] + ("class" | "struct")[opt] visibility[opt] + attribute-specifier-seq[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. @@ -3318,16 +3320,20 @@ def describe_signature(self, signode: TextElement, mode: str, class ASTClass(ASTBase): - def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass]) -> None: + def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass], + attrs: List[ASTAttribute]) -> None: self.name = name self.final = final self.bases = bases + self.attrs = attrs def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: return symbol.get_full_nested_name().get_id(version) def _stringify(self, transform: StringifyTransform) -> str: res = [] + for attr in self.attrs: + res.append(transform(attr) + ' ') res.append(transform(self.name)) if self.final: res.append(' final') @@ -3344,6 +3350,9 @@ def _stringify(self, transform: StringifyTransform) -> str: def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) + for attr in self.attrs: + attr.describe_signature(signode) + signode += addnodes.desc_sig_space() self.name.describe_signature(signode, mode, env, symbol=symbol) if self.final: signode += addnodes.desc_sig_space() @@ -3361,8 +3370,10 @@ def describe_signature(self, signode: TextElement, mode: str, class ASTUnion(ASTBase): - def __init__(self, name: ASTNestedName) -> None: + def __init__(self, name: ASTNestedName, + attrs: List[ASTAttribute]) -> None: self.name = name + self.attrs = attrs def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: if version == 1: @@ -3370,20 +3381,29 @@ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: return symbol.get_full_nested_name().get_id(version) def _stringify(self, transform: StringifyTransform) -> str: - return transform(self.name) + res = [] + for attr in self.attrs: + res.append(transform(attr) + ' ') + res.append(transform(self.name)) + return ''.join(res) def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) + for attr in self.attrs: + attr.describe_signature(signode) + signode += addnodes.desc_sig_space() self.name.describe_signature(signode, mode, env, symbol=symbol) class ASTEnum(ASTBase): def __init__(self, name: ASTNestedName, scoped: str, - underlyingType: ASTType) -> None: + underlyingType: ASTType, + attrs: List[ASTAttribute]) -> None: self.name = name self.scoped = scoped self.underlyingType = underlyingType + self.attrs = attrs def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: if version == 1: @@ -3395,6 +3415,8 @@ def _stringify(self, transform: StringifyTransform) -> str: if self.scoped: res.append(self.scoped) res.append(' ') + for attr in self.attrs: + res.append(transform(attr) + ' ') res.append(transform(self.name)) if self.underlyingType: res.append(' : ') @@ -3405,6 +3427,9 @@ def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) # self.scoped has been done by the CPPEnumObject + for attr in self.attrs: + attr.describe_signature(signode) + signode += addnodes.desc_sig_space() self.name.describe_signature(signode, mode, env, symbol=symbol) if self.underlyingType: signode += addnodes.desc_sig_space() @@ -6567,6 +6592,12 @@ def _parse_concept(self) -> ASTConcept: return ASTConcept(nestedName, initializer) def _parse_class(self) -> ASTClass: + attrs = [] + while 1: + attr = self._parse_attribute() + if attr is None: + break + attrs.append(attr) name = self._parse_nested_name() self.skip_ws() final = self.skip_word_and_ws('final') @@ -6594,21 +6625,33 @@ def _parse_class(self) -> ASTClass: continue else: break - return ASTClass(name, final, bases) + return ASTClass(name, final, bases, attrs) def _parse_union(self) -> ASTUnion: + attrs = [] + while 1: + attr = self._parse_attribute() + if attr is None: + break + attrs.append(attr) name = self._parse_nested_name() - return ASTUnion(name) + return ASTUnion(name, attrs) def _parse_enum(self) -> ASTEnum: scoped = None # is set by CPPEnumObject + attrs = [] + while 1: + attr = self._parse_attribute() + if attr is None: + break + attrs.append(attr) self.skip_ws() name = self._parse_nested_name() self.skip_ws() underlyingType = None if self.skip_string(':'): underlyingType = self._parse_type(named=False) - return ASTEnum(name, scoped, underlyingType) + return ASTEnum(name, scoped, underlyingType, attrs) def _parse_enumerator(self) -> ASTEnumerator: name = self._parse_nested_name() diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index a86cb6f4b56..dd2bde03274 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -15,6 +15,7 @@ from docutils import nodes from docutils.nodes import TextElement +from sphinx import addnodes from sphinx.config import Config from sphinx.util import logging @@ -134,8 +135,9 @@ def _stringify(self, transform: StringifyTransform) -> str: return "[[" + self.arg + "]]" def describe_signature(self, signode: TextElement) -> None: - txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode.append(addnodes.desc_sig_punctuation('[[', '[[')) + signode.append(nodes.Text(self.arg, self.arg)) + signode.append(addnodes.desc_sig_punctuation(']]', ']]')) class ASTGnuAttribute(ASTBaseBase): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 8fc974f4949..e811af335ae 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -996,6 +996,12 @@ def test_domain_cpp_ast_attributes(): # position: parameters and qualifiers check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'}) + # position: class + check('class', '{key}[[nodiscard]] Foo', {1: 'Foo', 2: '3Foo'}, key='class') + check('union', '{key}[[nodiscard]] Foo', {1: None, 2: '3Foo'}, key='union') + # position: enum + check('enum', '{key}[[nodiscard]] Foo', {1: None, 2: '3Foo'}, key='enum') + def test_domain_cpp_ast_xref_parsing(): def check(target):