diff --git a/CHANGES b/CHANGES index b22e05b8f09..e274b8b094c 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,8 @@ Deprecated Features added -------------- +* #7853: C and C++, support parameterized GNU style attributes. + Bugs fixed ---------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 36a8f1f6593..932c53fdd55 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -28,7 +28,8 @@ from sphinx.roles import SphinxRole, XRefRole from sphinx.util import logging from sphinx.util.cfamily import ( - NoOldIdError, ASTBaseBase, verify_description_mode, StringifyTransform, + NoOldIdError, ASTBaseBase, ASTBaseParenExprList, + verify_description_mode, StringifyTransform, BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral, identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re, hex_literal_re, binary_literal_re, integers_literal_suffix_re, @@ -1053,7 +1054,7 @@ def describe_signature(self, signode: TextElement, mode: str, # Initializer ################################################################################ -class ASTParenExprList(ASTBase): +class ASTParenExprList(ASTBaseParenExprList): def __init__(self, exprs: List[ASTExpression]) -> None: self.exprs = exprs diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 1783db4916d..0d76172a037 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -31,7 +31,8 @@ from sphinx.transforms.post_transforms import ReferencesResolver from sphinx.util import logging from sphinx.util.cfamily import ( - NoOldIdError, ASTBaseBase, ASTAttribute, verify_description_mode, StringifyTransform, + NoOldIdError, ASTBaseBase, ASTAttribute, ASTBaseParenExprList, + verify_description_mode, StringifyTransform, BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral, identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re, hex_literal_re, binary_literal_re, integers_literal_suffix_re, @@ -2742,7 +2743,7 @@ def describe_signature(self, signode: TextElement, mode: str, signode += nodes.Text('...') -class ASTParenExprList(ASTBase): +class ASTParenExprList(ASTBaseParenExprList): def __init__(self, exprs: List[Union[ASTExpression, ASTBracedInitList]]) -> None: self.exprs = exprs diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index edccf96a713..6c2e99c8425 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -12,7 +12,7 @@ import warnings from copy import deepcopy from typing import ( - Any, Callable, List, Match, Pattern, Tuple, Union + Any, Callable, List, Match, Optional, Pattern, Tuple, Union ) from docutils import nodes @@ -148,16 +148,14 @@ def describe_signature(self, signode: TextElement) -> None: class ASTGnuAttribute(ASTBaseBase): - def __init__(self, name: str, args: Any) -> None: + def __init__(self, name: str, args: Optional["ASTBaseParenExprList"]) -> None: self.name = name self.args = args def _stringify(self, transform: StringifyTransform) -> str: res = [self.name] if self.args: - res.append('(') res.append(transform(self.args)) - res.append(')') return ''.join(res) @@ -211,6 +209,11 @@ def describe_signature(self, signode: TextElement) -> None: ################################################################################ +class ASTBaseParenExprList(ASTBaseBase): + pass + + +################################################################################ class UnsupportedMultiCharacterCharLiteral(Exception): @property @@ -415,11 +418,8 @@ def _parse_attribute(self) -> ASTAttribute: while 1: if self.match(identifier_re): name = self.matched_text - self.skip_ws() - if self.skip_string_and_ws('('): - self.fail('Parameterized GNU style attribute not yet supported.') - attrs.append(ASTGnuAttribute(name, None)) - # TODO: parse arguments for the attribute + exprs = self._parse_paren_expression_list() + attrs.append(ASTGnuAttribute(name, exprs)) if self.skip_string_and_ws(','): continue elif self.skip_string_and_ws(')'): @@ -447,3 +447,6 @@ def _parse_attribute(self) -> ASTAttribute: return ASTParenAttribute(id, arg) return None + + def _parse_paren_expression_list(self) -> ASTBaseParenExprList: + raise NotImplementedError diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 10a20b9d155..347f8c7d6a5 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -469,6 +469,8 @@ def test_attributes(): check('member', '__attribute__(()) int f', {1: 'f'}) check('member', '__attribute__((a)) int f', {1: 'f'}) check('member', '__attribute__((a, b)) int f', {1: 'f'}) + check('member', '__attribute__((optimize(3))) int f', {1: 'f'}) + check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f'}) # style: user-defined id check('member', 'id_attr int f', {1: 'f'}) # style: user-defined paren diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 961646131b8..a6c9eee9d1b 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -897,6 +897,8 @@ def test_attributes(): check('member', '__attribute__(()) int f', {1: 'f__i', 2: '1f'}) check('member', '__attribute__((a)) int f', {1: 'f__i', 2: '1f'}) check('member', '__attribute__((a, b)) int f', {1: 'f__i', 2: '1f'}) + check('member', '__attribute__((optimize(3))) int f', {1: 'f__i', 2: '1f'}) + check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f__i', 2: '1f'}) # style: user-defined id check('member', 'id_attr int f', {1: 'f__i', 2: '1f'}) # style: user-defined paren