C and C++, refactor attribute lists

This commit is contained in:
Jakob Lykke Andersen
2022-04-17 17:17:19 +02:00
parent d951e55bc3
commit 991fe26fa5
5 changed files with 118 additions and 120 deletions

View File

@@ -21,8 +21,8 @@ from sphinx.roles import SphinxRole, XRefRole
from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import ReferencesResolver
from sphinx.util import logging
from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList, BaseParser,
DefinitionError, NoOldIdError, StringifyTransform,
from sphinx.util.cfamily import (ASTAttributeList, ASTBaseBase, ASTBaseParenExprList,
BaseParser, DefinitionError, NoOldIdError, StringifyTransform,
UnsupportedMultiCharacterCharLiteral, anon_identifier_re,
binary_literal_re, char_literal_re, float_literal_re,
float_literal_suffix_re, hex_literal_re, identifier_re,
@@ -687,7 +687,7 @@ class ASTFunctionParameter(ASTBase):
class ASTParameters(ASTBase):
def __init__(self, args: List[ASTFunctionParameter], attrs: List[ASTAttribute]) -> None:
def __init__(self, args: List[ASTFunctionParameter], attrs: ASTAttributeList) -> None:
self.args = args
self.attrs = attrs
@@ -705,9 +705,9 @@ class ASTParameters(ASTBase):
first = False
res.append(str(a))
res.append(')')
for attr in self.attrs:
if len(self.attrs) != 0:
res.append(' ')
res.append(transform(attr))
res.append(transform(self.attrs))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
@@ -732,14 +732,14 @@ class ASTParameters(ASTBase):
arg.describe_signature(signode, 'markType', env, symbol=symbol)
signode += addnodes.desc_sig_punctuation(')', ')')
for attr in self.attrs:
if len(self.attrs) != 0:
signode += addnodes.desc_sig_space()
attr.describe_signature(signode)
self.attrs.describe_signature(signode)
class ASTDeclSpecsSimple(ASTBaseBase):
def __init__(self, storage: str, threadLocal: str, inline: bool,
restrict: bool, volatile: bool, const: bool, attrs: List[Any]) -> None:
restrict: bool, volatile: bool, const: bool, attrs: ASTAttributeList) -> None:
self.storage = storage
self.threadLocal = threadLocal
self.inline = inline
@@ -761,7 +761,8 @@ class ASTDeclSpecsSimple(ASTBaseBase):
def _stringify(self, transform: StringifyTransform) -> str:
res: List[str] = []
res.extend(transform(attr) for attr in self.attrs)
if len(self.attrs) != 0:
res.append(transform(self.attrs))
if self.storage:
res.append(self.storage)
if self.threadLocal:
@@ -778,14 +779,15 @@ class ASTDeclSpecsSimple(ASTBaseBase):
def describe_signature(self, modifiers: List[Node]) -> None:
def _add(modifiers: List[Node], text: str) -> None:
if len(modifiers) > 0:
if len(modifiers) != 0:
modifiers.append(addnodes.desc_sig_space())
modifiers.append(addnodes.desc_sig_keyword(text, text))
for attr in self.attrs:
if len(modifiers) > 0:
if len(modifiers) != 0 and len(self.attrs) != 0:
modifiers.append(addnodes.desc_sig_space())
modifiers.append(attr.describe_signature(modifiers))
tempNode = nodes.TextElement()
self.attrs.describe_signature(tempNode)
modifiers.extend(tempNode.children)
if self.storage:
_add(modifiers, self.storage)
if self.threadLocal:
@@ -1002,7 +1004,7 @@ class ASTDeclaratorNameBitField(ASTDeclarator):
class ASTDeclaratorPtr(ASTDeclarator):
def __init__(self, next: ASTDeclarator, restrict: bool, volatile: bool, const: bool,
attrs: Any) -> None:
attrs: ASTAttributeList) -> None:
assert next
self.next = next
self.restrict = restrict
@@ -1025,9 +1027,8 @@ class ASTDeclaratorPtr(ASTDeclarator):
def _stringify(self, transform: StringifyTransform) -> str:
res = ['*']
for a in self.attrs:
res.append(transform(a))
if len(self.attrs) > 0 and (self.restrict or self.volatile or self.const):
res.append(transform(self.attrs))
if len(self.attrs) != 0 and (self.restrict or self.volatile or self.const):
res.append(' ')
if self.restrict:
res.append('restrict')
@@ -1049,9 +1050,8 @@ class ASTDeclaratorPtr(ASTDeclarator):
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('*', '*')
for a in self.attrs:
a.describe_signature(signode)
if len(self.attrs) > 0 and (self.restrict or self.volatile or self.const):
self.attrs.describe_signature(signode)
if len(self.attrs) != 0 and (self.restrict or self.volatile or self.const):
signode += addnodes.desc_sig_space()
def _add_anno(signode: TextElement, text: str) -> None:
@@ -2641,13 +2641,7 @@ class DefinitionParser(BaseParser):
'Expecting "," or ")" in parameters, '
'got "%s".' % self.current_char)
attrs = []
while True:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
attrs = self._parse_attribute_list()
return ASTParameters(args, attrs)
def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
@@ -2706,7 +2700,7 @@ class DefinitionParser(BaseParser):
continue
break
return ASTDeclSpecsSimple(storage, threadLocal, inline,
restrict, volatile, const, attrs)
restrict, volatile, const, ASTAttributeList(attrs))
def _parse_decl_specs(self, outer: str, typed: bool = True) -> ASTDeclSpecs:
if outer:
@@ -2838,7 +2832,7 @@ class DefinitionParser(BaseParser):
next = self._parse_declarator(named, paramMode, typed)
return ASTDeclaratorPtr(next=next,
restrict=restrict, volatile=volatile, const=const,
attrs=attrs)
attrs=ASTAttributeList(attrs))
if typed and self.current_char == '(': # note: peeking, not skipping
# maybe this is the beginning of params, try that first,
# otherwise assume it's noptr->declarator > ( ptr-declarator )

View File

@@ -21,8 +21,8 @@ from sphinx.roles import SphinxRole, XRefRole
from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import ReferencesResolver
from sphinx.util import logging
from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList, BaseParser,
DefinitionError, NoOldIdError, StringifyTransform,
from sphinx.util.cfamily import (ASTAttributeList, ASTBaseBase, ASTBaseParenExprList,
BaseParser, DefinitionError, NoOldIdError, StringifyTransform,
UnsupportedMultiCharacterCharLiteral, anon_identifier_re,
binary_literal_re, char_literal_re, float_literal_re,
float_literal_suffix_re, hex_literal_re, identifier_re,
@@ -2048,7 +2048,7 @@ class ASTParametersQualifiers(ASTBase):
def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool,
refQual: Optional[str], exceptionSpec: ASTNoexceptSpec,
trailingReturn: "ASTType",
override: bool, final: bool, attrs: List[ASTAttribute],
override: bool, final: bool, attrs: ASTAttributeList,
initializer: Optional[str]) -> None:
self.args = args
self.volatile = volatile
@@ -2118,9 +2118,9 @@ class ASTParametersQualifiers(ASTBase):
res.append(' final')
if self.override:
res.append(' override')
for attr in self.attrs:
if len(self.attrs) != 0:
res.append(' ')
res.append(transform(attr))
res.append(transform(self.attrs))
if self.initializer:
res.append(' = ')
res.append(self.initializer)
@@ -2171,9 +2171,9 @@ class ASTParametersQualifiers(ASTBase):
_add_anno(signode, 'final')
if self.override:
_add_anno(signode, 'override')
for attr in self.attrs:
if len(self.attrs) != 0:
signode += addnodes.desc_sig_space()
attr.describe_signature(signode)
self.attrs.describe_signature(signode)
if self.initializer:
signode += addnodes.desc_sig_space()
signode += addnodes.desc_sig_punctuation('=', '=')
@@ -2211,7 +2211,7 @@ class ASTDeclSpecsSimple(ASTBase):
explicitSpec: Optional[ASTExplicitSpec],
consteval: bool, constexpr: bool, constinit: bool,
volatile: bool, const: bool, friend: bool,
attrs: List[ASTAttribute]) -> None:
attrs: ASTAttributeList) -> None:
self.storage = storage
self.threadLocal = threadLocal
self.inline = inline
@@ -2243,7 +2243,8 @@ class ASTDeclSpecsSimple(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
res: List[str] = []
res.extend(transform(attr) for attr in self.attrs)
if len(self.attrs) != 0:
res.append(transform(self.attrs))
if self.storage:
res.append(self.storage)
if self.threadLocal:
@@ -2270,12 +2271,8 @@ class ASTDeclSpecsSimple(ASTBase):
def describe_signature(self, signode: TextElement,
env: "BuildEnvironment", symbol: "Symbol") -> None:
addSpace = False
for attr in self.attrs:
if addSpace:
signode += addnodes.desc_sig_space()
addSpace = True
attr.describe_signature(signode)
self.attrs.describe_signature(signode)
addSpace = len(self.attrs) != 0
def _add(signode: TextElement, text: str) -> bool:
if addSpace:
@@ -2592,7 +2589,7 @@ class ASTDeclaratorNameBitField(ASTDeclarator):
class ASTDeclaratorPtr(ASTDeclarator):
def __init__(self, next: ASTDeclarator, volatile: bool, const: bool,
attrs: List[ASTAttribute]) -> None:
attrs: ASTAttributeList) -> None:
assert next
self.next = next
self.volatile = volatile
@@ -2620,9 +2617,8 @@ class ASTDeclaratorPtr(ASTDeclarator):
def _stringify(self, transform: StringifyTransform) -> str:
res = ['*']
for a in self.attrs:
res.append(transform(a))
if len(self.attrs) > 0 and (self.volatile or self.const):
res.append(transform(self.attrs))
if len(self.attrs) != 0 and (self.volatile or self.const):
res.append(' ')
if self.volatile:
res.append('volatile')
@@ -2677,9 +2673,8 @@ class ASTDeclaratorPtr(ASTDeclarator):
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('*', '*')
for a in self.attrs:
a.describe_signature(signode)
if len(self.attrs) > 0 and (self.volatile or self.const):
self.attrs.describe_signature(signode)
if len(self.attrs) != 0 and (self.volatile or self.const):
signode += addnodes.desc_sig_space()
def _add_anno(signode: TextElement, text: str) -> None:
@@ -2697,7 +2692,7 @@ class ASTDeclaratorPtr(ASTDeclarator):
class ASTDeclaratorRef(ASTDeclarator):
def __init__(self, next: ASTDeclarator, attrs: List[ASTAttribute]) -> None:
def __init__(self, next: ASTDeclarator, attrs: ASTAttributeList) -> None:
assert next
self.next = next
self.attrs = attrs
@@ -2727,9 +2722,8 @@ class ASTDeclaratorRef(ASTDeclarator):
def _stringify(self, transform: StringifyTransform) -> str:
res = ['&']
for a in self.attrs:
res.append(transform(a))
if len(self.attrs) > 0 and self.next.require_space_after_declSpecs():
res.append(transform(self.attrs))
if len(self.attrs) != 0 and self.next.require_space_after_declSpecs():
res.append(' ')
res.append(transform(self.next))
return ''.join(res)
@@ -2758,8 +2752,7 @@ class ASTDeclaratorRef(ASTDeclarator):
env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('&', '&')
for a in self.attrs:
a.describe_signature(signode)
self.attrs.describe_signature(signode)
if len(self.attrs) > 0 and self.next.require_space_after_declSpecs():
signode += addnodes.desc_sig_space()
self.next.describe_signature(signode, mode, env, symbol)
@@ -3349,7 +3342,7 @@ class ASTBaseClass(ASTBase):
class ASTClass(ASTBase):
def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass],
attrs: List[ASTAttribute]) -> None:
attrs: ASTAttributeList) -> None:
self.name = name
self.final = final
self.bases = bases
@@ -3360,8 +3353,9 @@ class ASTClass(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
res = []
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.attrs))
if len(self.attrs) != 0:
res.append(' ')
res.append(transform(self.name))
if self.final:
res.append(' final')
@@ -3378,8 +3372,8 @@ class ASTClass(ASTBase):
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)
self.attrs.describe_signature(signode)
if len(self.attrs) != 0:
signode += addnodes.desc_sig_space()
self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.final:
@@ -3398,7 +3392,7 @@ class ASTClass(ASTBase):
class ASTUnion(ASTBase):
def __init__(self, name: ASTNestedName, attrs: List[ASTAttribute]) -> None:
def __init__(self, name: ASTNestedName, attrs: ASTAttributeList) -> None:
self.name = name
self.attrs = attrs
@@ -3409,23 +3403,24 @@ class ASTUnion(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
res = []
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.attrs))
if len(self.attrs) != 0:
res.append(' ')
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)
self.attrs.describe_signature(signode)
if len(self.attrs) != 0:
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,
attrs: List[ASTAttribute]) -> None:
attrs: ASTAttributeList) -> None:
self.name = name
self.scoped = scoped
self.underlyingType = underlyingType
@@ -3441,8 +3436,9 @@ class ASTEnum(ASTBase):
if self.scoped:
res.append(self.scoped)
res.append(' ')
for attr in self.attrs:
res.append(transform(attr) + ' ')
res.append(transform(self.attrs))
if len(self.attrs) != 0:
res.append(' ')
res.append(transform(self.name))
if self.underlyingType:
res.append(' : ')
@@ -3453,8 +3449,8 @@ class ASTEnum(ASTBase):
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)
self.attrs.describe_signature(signode)
if len(self.attrs) != 0:
signode += addnodes.desc_sig_space()
self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.underlyingType:
@@ -6118,12 +6114,7 @@ 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)
attrs = self._parse_attribute_list()
self.skip_ws()
initializer = None
@@ -6235,7 +6226,7 @@ class DefinitionParser(BaseParser):
break
return ASTDeclSpecsSimple(storage, threadLocal, inline, virtual,
explicitSpec, consteval, constexpr, constinit,
volatile, const, friend, attrs)
volatile, const, friend, ASTAttributeList(attrs))
def _parse_decl_specs(self, outer: str, typed: bool = True) -> ASTDeclSpecs:
if outer:
@@ -6332,7 +6323,7 @@ class DefinitionParser(BaseParser):
self.skip_ws()
volatile = False
const = False
attrs = []
attrList = []
while 1:
if not volatile:
volatile = self.skip_word_and_ws('volatile')
@@ -6344,19 +6335,15 @@ class DefinitionParser(BaseParser):
continue
attr = self._parse_attribute()
if attr is not None:
attrs.append(attr)
attrList.append(attr)
continue
break
next = self._parse_declarator(named, paramMode, typed)
return ASTDeclaratorPtr(next=next, volatile=volatile, const=const, attrs=attrs)
return ASTDeclaratorPtr(next=next, volatile=volatile, const=const,
attrs=ASTAttributeList(attrList))
# TODO: shouldn't we parse an R-value ref here first?
if typed and self.skip_string("&"):
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
attrs = self._parse_attribute_list()
next = self._parse_declarator(named, paramMode, typed)
return ASTDeclaratorRef(next=next, attrs=attrs)
if typed and self.skip_string("..."):
@@ -6628,12 +6615,7 @@ class DefinitionParser(BaseParser):
return ASTConcept(nestedName, initializer)
def _parse_class(self) -> ASTClass:
attrs = []
while 1:
attr = self._parse_attribute()
if attr is None:
break
attrs.append(attr)
attrs = self._parse_attribute_list()
name = self._parse_nested_name()
self.skip_ws()
final = self.skip_word_and_ws('final')
@@ -6664,24 +6646,13 @@ class DefinitionParser(BaseParser):
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)
attrs = self._parse_attribute_list()
name = self._parse_nested_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()
attrs = self._parse_attribute_list()
name = self._parse_nested_name()
self.skip_ws()
underlyingType = None

View File

@@ -192,6 +192,30 @@ class ASTParenAttribute(ASTAttribute):
signode.append(nodes.Text(txt, txt))
class ASTAttributeList(ASTBaseBase):
def __init__(self, attrs: List[ASTAttribute]) -> None:
self.attrs = attrs
def __len__(self) -> int:
return len(self.attrs)
def __add__(self, other: "ASTAttributeList") -> "ASTAttributeList":
return ASTAttributeList(self.attrs + other.attrs)
def _stringify(self, transform: StringifyTransform) -> str:
return ' '.join(transform(attr) for attr in self.attrs)
def describe_signature(self, signode: TextElement) -> None:
if len(self.attrs) == 0:
return
self.attrs[0].describe_signature(signode)
if len(self.attrs) == 1:
return
for attr in self.attrs[1:]:
signode.append(addnodes.desc_sig_space())
attr.describe_signature(signode)
################################################################################
class ASTBaseParenExprList(ASTBaseBase):
@@ -423,5 +447,14 @@ class BaseParser:
return None
def _parse_attribute_list(self) -> ASTAttributeList:
res = []
while True:
attr = self._parse_attribute()
if attr is None:
break
res.append(attr)
return ASTAttributeList(res)
def _parse_paren_expression_list(self) -> ASTBaseParenExprList:
raise NotImplementedError

View File

@@ -573,10 +573,10 @@ def test_domain_c_ast_attributes():
output='__attribute__(()) static inline 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'})
check('member', 'int *[[attr1]] [[attr2]] i', {1: 'i'})
check('member', 'int *const [[attr1]] [[attr2]] volatile i', {1: 'i'},
output='int *[[attr1]] [[attr2]] volatile const i')
check('member', 'int *[[attr1]] [[attr2]] *i', {1: 'i'})
# position: parameters
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'})

View File

@@ -983,18 +983,18 @@ def test_domain_cpp_ast_attributes():
output='__attribute__(()) static inline 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'})
check('member', 'int *[[attr1]] [[attr2]] i', {1: 'i__iP', 2: '1i'})
check('member', 'int *const [[attr1]] [[attr2]] volatile i', {1: 'i__iPVC', 2: '1i'},
output='int *[[attr1]] [[attr2]] volatile const i')
check('member', 'int &[[attr1]] [[attr2]] i', {1: 'i__iR', 2: '1i'})
check('member', 'int *[[attr1]] [[attr2]] *i', {1: 'i__iPP', 2: '1i'})
# position: parameters and qualifiers
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'})
# position: class, union, enum
check('class', '{key}[[nodiscard]] Foo', {1: 'Foo', 2: '3Foo'}, key='class')
check('union', '{key}[[nodiscard]] Foo', {1: None, 2: '3Foo'}, key='union')
check('enum', '{key}[[nodiscard]] Foo', {1: None, 2: '3Foo'}, key='enum')
check('class', '{key}[[attr1]] [[attr2]] Foo', {1: 'Foo', 2: '3Foo'}, key='class')
check('union', '{key}[[attr1]] [[attr2]] Foo', {1: None, 2: '3Foo'}, key='union')
check('enum', '{key}[[attr1]] [[attr2]] Foo', {1: None, 2: '3Foo'}, key='enum')
def test_domain_cpp_ast_xref_parsing():