summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Lykke Andersen <Jakob@caput.dk>2020-08-13 18:39:28 +0200
committerJakob Lykke Andersen <Jakob@caput.dk>2020-08-13 18:39:28 +0200
commit52140be6b8d32795aaf99d4535121eaea6d8b3b3 (patch)
treebec0dc287fd09bdc48427c4e023110fe5baad922
parent667a188e25cb70a1c673081b066a8a90c6e8eea7 (diff)
downloadsphinx-git-52140be6b8d32795aaf99d4535121eaea6d8b3b3.tar.gz
C and C++, parsing function attributes
Fixes sphinx-doc/sphinx#8114
-rw-r--r--CHANGES1
-rw-r--r--sphinx/domains/c.py21
-rw-r--r--sphinx/domains/cpp.py19
-rw-r--r--sphinx/util/cfamily.py2
-rw-r--r--tests/test_domain_c.py9
-rw-r--r--tests/test_domain_cpp.py6
6 files changed, 44 insertions, 14 deletions
diff --git a/CHANGES b/CHANGES
index 282646967..50008f519 100644
--- a/CHANGES
+++ b/CHANGES
@@ -36,6 +36,7 @@ Features added
* #8095: napoleon: Add :confval:`napoleon_preprocess_types` to enable the type
preprocessor for numpy style docstrings
+* #8114: C and C++, parse function attributes after parameters and qualifiers.
Bugs fixed
----------
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 65786b5de..d8ccc2e3d 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -32,7 +32,7 @@ from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import ReferencesResolver
from sphinx.util import logging
from sphinx.util.cfamily import (
- NoOldIdError, ASTBaseBase, ASTBaseParenExprList,
+ NoOldIdError, ASTBaseBase, ASTAttribute, ASTBaseParenExprList,
verify_description_mode, StringifyTransform,
BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral,
identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re,
@@ -652,8 +652,9 @@ class ASTFunctionParameter(ASTBase):
class ASTParameters(ASTBase):
- def __init__(self, args: List[ASTFunctionParameter]) -> None:
+ def __init__(self, args: List[ASTFunctionParameter], attrs: List[ASTAttribute]) -> None:
self.args = args
+ self.attrs = attrs
@property
def function_params(self) -> List[ASTFunctionParameter]:
@@ -669,6 +670,9 @@ class ASTParameters(ASTBase):
first = False
res.append(str(a))
res.append(')')
+ for attr in self.attrs:
+ res.append(' ')
+ res.append(transform(attr))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
@@ -683,6 +687,9 @@ class ASTParameters(ASTBase):
arg.describe_signature(param, 'markType', env, symbol=symbol)
paramlist += param
signode += paramlist
+ for attr in self.attrs:
+ signode += nodes.Text(' ')
+ attr.describe_signature(signode)
class ASTDeclSpecsSimple(ASTBaseBase):
@@ -2572,7 +2579,15 @@ class DefinitionParser(BaseParser):
self.fail(
'Expecting "," or ")" in parameters, '
'got "%s".' % self.current_char)
- return ASTParameters(args)
+
+ attrs = []
+ while True:
+ attr = self._parse_attribute()
+ if attr is None:
+ break
+ attrs.append(attr)
+
+ return ASTParameters(args, attrs)
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
"""Just parse the simple ones."""
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 0b3c02e83..92d578427 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -1879,7 +1879,8 @@ class ASTNoexceptSpec(ASTBase):
class ASTParametersQualifiers(ASTBase):
def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool,
refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType",
- override: bool, final: bool, initializer: str) -> None:
+ override: bool, final: bool, attrs: List[ASTAttribute],
+ initializer: str) -> None:
self.args = args
self.volatile = volatile
self.const = const
@@ -1888,6 +1889,7 @@ class ASTParametersQualifiers(ASTBase):
self.trailingReturn = trailingReturn
self.override = override
self.final = final
+ self.attrs = attrs
self.initializer = initializer
@property
@@ -1947,6 +1949,9 @@ class ASTParametersQualifiers(ASTBase):
res.append(' final')
if self.override:
res.append(' override')
+ for attr in self.attrs:
+ res.append(' ')
+ res.append(transform(attr))
if self.initializer:
res.append(' = ')
res.append(self.initializer)
@@ -1988,6 +1993,9 @@ class ASTParametersQualifiers(ASTBase):
_add_anno(signode, 'final')
if self.override:
_add_anno(signode, 'override')
+ for attr in self.attrs:
+ signode += nodes.Text(' ')
+ attr.describe_signature(signode)
if self.initializer:
_add_text(signode, '= ' + str(self.initializer))
@@ -5709,6 +5717,13 @@ class DefinitionParser(BaseParser):
override = self.skip_word_and_ws(
'override') # they can be permuted
+ attrs = []
+ while True:
+ attr = self._parse_attribute()
+ if attr is None:
+ break
+ attrs.append(attr)
+
self.skip_ws()
initializer = None
if self.skip_string('='):
@@ -5725,7 +5740,7 @@ class DefinitionParser(BaseParser):
return ASTParametersQualifiers(
args, volatile, const, refQual, exceptionSpec, trailingReturn,
- override, final, initializer)
+ override, final, attrs, initializer)
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
"""Just parse the simple ones."""
diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py
index a67b2a0ad..0edea128c 100644
--- a/sphinx/util/cfamily.py
+++ b/sphinx/util/cfamily.py
@@ -391,7 +391,7 @@ class BaseParser:
% startPos)
return self.definition[startPos:self.pos]
- def _parse_attribute(self) -> ASTAttribute:
+ def _parse_attribute(self) -> Optional[ASTAttribute]:
self.skip_ws()
# try C++11 style
startPos = self.pos
diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py
index 71bf251e9..b6f72287e 100644
--- a/tests/test_domain_c.py
+++ b/tests/test_domain_c.py
@@ -497,17 +497,16 @@ def test_attributes():
parse('member', 'paren_attr({]}) int f')
# position: decl specs
- check('function', 'static inline __attribute__(()) void f()',
- {1: 'f'},
+ check('function', 'static inline __attribute__(()) void f()', {1: 'f'},
output='__attribute__(()) static inline void f()')
- check('function', '[[attr1]] [[attr2]] void f()',
- {1: 'f'},
- output='[[attr1]] [[attr2]] void f()')
+ check('function', '[[attr1]] [[attr2]] void f()', {1: 'f'})
# position: declarator
check('member', 'int *[[attr]] i', {1: 'i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i'},
output='int *[[attr]] volatile const i')
check('member', 'int *[[attr]] *i', {1: 'i'})
+ # position: parameters
+ check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'})
# issue michaeljones/breathe#500
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index 513205cab..558d69911 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -938,15 +938,15 @@ def test_attributes():
check('function', 'static inline __attribute__(()) void f()',
{1: 'f', 2: '1fv'},
output='__attribute__(()) static inline void f()')
- check('function', '[[attr1]] [[attr2]] void f()',
- {1: 'f', 2: '1fv'},
- output='[[attr1]] [[attr2]] void f()')
+ check('function', '[[attr1]] [[attr2]] void f()', {1: 'f', 2: '1fv'})
# position: declarator
check('member', 'int *[[attr]] i', {1: 'i__iP', 2: '1i'})
check('member', 'int *const [[attr]] volatile i', {1: 'i__iPVC', 2: '1i'},
output='int *[[attr]] volatile const i')
check('member', 'int &[[attr]] i', {1: 'i__iR', 2: '1i'})
check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'})
+ # position: parameters and qualifiers
+ check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'})
def test_xref_parsing():