Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C and C++, parsing function attributes #8117

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -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
----------
Expand Down
21 changes: 18 additions & 3 deletions sphinx/domains/c.py
Expand Up @@ -32,7 +32,7 @@
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,
Expand Down Expand Up @@ -652,8 +652,9 @@ def describe_signature(self, signode: Any, mode: str,


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]:
Expand All @@ -669,6 +670,9 @@ def _stringify(self, transform: StringifyTransform) -> str:
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,
Expand All @@ -683,6 +687,9 @@ def describe_signature(self, signode: TextElement, mode: str,
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):
Expand Down Expand Up @@ -2572,7 +2579,15 @@ def _parse_parameters(self, paramMode: str) -> ASTParameters:
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."""
Expand Down
19 changes: 17 additions & 2 deletions sphinx/domains/cpp.py
Expand Up @@ -1879,7 +1879,8 @@ def describe_signature(self, signode: TextElement, mode: str,
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
Expand All @@ -1888,6 +1889,7 @@ def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool
self.trailingReturn = trailingReturn
self.override = override
self.final = final
self.attrs = attrs
self.initializer = initializer

@property
Expand Down Expand Up @@ -1947,6 +1949,9 @@ def _stringify(self, transform: StringifyTransform) -> str:
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)
Expand Down Expand Up @@ -1988,6 +1993,9 @@ def _add_text(signode: TextElement, text: str) -> None:
_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))

Expand Down Expand Up @@ -5709,6 +5717,13 @@ def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQuali
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('='):
Expand All @@ -5725,7 +5740,7 @@ def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQuali

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."""
Expand Down
2 changes: 1 addition & 1 deletion sphinx/util/cfamily.py
Expand Up @@ -391,7 +391,7 @@ def _parse_balanced_token_seq(self, end: List[str]) -> str:
% 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
Expand Down
9 changes: 4 additions & 5 deletions tests/test_domain_c.py
Expand Up @@ -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)',
Expand Down
6 changes: 3 additions & 3 deletions tests/test_domain_cpp.py
Expand Up @@ -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():
Expand Down