diff --git a/CHANGES b/CHANGES index 4855771ea8f..7e9a2c486e7 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,8 @@ Deprecated Features added -------------- +* C, parse array declarators with static, qualifiers, and VLA specification. + Bugs fixed ---------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 53dd3ab2a21..6593b6db38c 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -791,20 +791,60 @@ def _add(modifiers: List[Node], text: str) -> None: ################################################################################ class ASTArray(ASTBase): - def __init__(self, size: ASTExpression): + def __init__(self, static: bool, const: bool, volatile: bool, restrict: bool, + vla: bool, size: ASTExpression): + self.static = static + self.const = const + self.volatile = volatile + self.restrict = restrict + self.vla = vla self.size = size + if vla: + assert size is None + if size is not None: + assert not vla def _stringify(self, transform: StringifyTransform) -> str: - if self.size: - return '[' + transform(self.size) + ']' - else: - return '[]' + el = [] + if self.static: + el.append('static') + if self.restrict: + el.append('restrict') + if self.volatile: + el.append('volatile') + if self.const: + el.append('const') + if self.vla: + return '[' + ' '.join(el) + '*]' + elif self.size: + el.append(transform(self.size)) + return '[' + ' '.join(el) + ']' def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) signode.append(nodes.Text("[")) - if self.size: + addSpace = False + + def _add(signode: TextElement, text: str) -> bool: + if addSpace: + signode += nodes.Text(' ') + signode += addnodes.desc_annotation(text, text) + return True + + if self.static: + addSpace = _add(signode, 'static') + if self.restrict: + addSpace = _add(signode, 'restrict') + if self.volatile: + addSpace = _add(signode, 'volatile') + if self.const: + addSpace = _add(signode, 'const') + if self.vla: + signode.append(nodes.Text('*')) + elif self.size: + if addSpace: + signode += nodes.Text(' ') self.size.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text("]")) @@ -2587,18 +2627,45 @@ def _parse_declarator_name_suffix( self.skip_ws() if typed and self.skip_string('['): self.skip_ws() - if self.skip_string(']'): - arrayOps.append(ASTArray(None)) - continue - - def parser(): - return self._parse_expression() + static = False + const = False + volatile = False + restrict = False + while True: + if not static: + if self.skip_word_and_ws('static'): + static = True + continue + if not const: + if self.skip_word_and_ws('const'): + const = True + continue + if not volatile: + if self.skip_word_and_ws('volatile'): + volatile = True + continue + if not restrict: + if self.skip_word_and_ws('restrict'): + restrict = True + continue + break + vla = False if static else self.skip_string_and_ws('*') + if vla: + if not self.skip_string(']'): + self.fail("Expected ']' in end of array operator.") + size = None + else: + if self.skip_string(']'): + size = None + else: - value = self._parse_expression_fallback([']'], parser) - if not self.skip_string(']'): - self.fail("Expected ']' in end of array operator.") - arrayOps.append(ASTArray(value)) - continue + def parser(): + return self._parse_expression() + size = self._parse_expression_fallback([']'], parser) + self.skip_ws() + if not self.skip_string(']'): + self.fail("Expected ']' in end of array operator.") + arrayOps.append(ASTArray(static, const, volatile, restrict, vla, size)) else: break param = self._parse_parameters(paramMode) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 009f51644d4..d89e169c358 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -354,6 +354,21 @@ def test_function_definitions(): check('function', 'void f(enum E e)', {1: 'f'}) check('function', 'void f(union E e)', {1: 'f'}) + # array declarators + check('function', 'void f(int arr[])', {1: 'f'}) + check('function', 'void f(int arr[*])', {1: 'f'}) + cvrs = ['', 'const', 'volatile', 'restrict', 'restrict volatile const'] + for cvr in cvrs: + space = ' ' if len(cvr) != 0 else '' + check('function', 'void f(int arr[{}*])'.format(cvr), {1: 'f'}) + check('function', 'void f(int arr[{}])'.format(cvr), {1: 'f'}) + check('function', 'void f(int arr[{}{}42])'.format(cvr, space), {1: 'f'}) + check('function', 'void f(int arr[static{}{} 42])'.format(space, cvr), {1: 'f'}) + check('function', 'void f(int arr[{}{}static 42])'.format(cvr, space), {1: 'f'}, + output='void f(int arr[static{}{} 42])'.format(space, cvr)) + check('function', 'void f(int arr[const static volatile 42])', {1: 'f'}, + output='void f(int arr[static volatile const 42])') + def test_union_definitions(): check('struct', 'A', {1: 'A'})