diff options
author | Jakob Lykke Andersen <Jakob@caput.dk> | 2022-04-17 17:17:19 +0200 |
---|---|---|
committer | Jakob Lykke Andersen <Jakob@caput.dk> | 2022-04-17 17:17:19 +0200 |
commit | 991fe26fa5683bb76a925389024d375c6bb11dba (patch) | |
tree | ce60b5bc9d132660e599b03b9f8a65bb7a4d233e | |
parent | d951e55bc3419dbda809ed0aca17addeed8e9e30 (diff) | |
download | sphinx-git-991fe26fa5683bb76a925389024d375c6bb11dba.tar.gz |
C and C++, refactor attribute lists
-rw-r--r-- | sphinx/domains/c.py | 54 | ||||
-rw-r--r-- | sphinx/domains/cpp.py | 127 | ||||
-rw-r--r-- | sphinx/util/cfamily.py | 33 | ||||
-rw-r--r-- | tests/test_domain_c.py | 8 | ||||
-rw-r--r-- | tests/test_domain_cpp.py | 16 |
5 files changed, 118 insertions, 120 deletions
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 532eebe52..22280dfd5 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -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: - modifiers.append(addnodes.desc_sig_space()) - modifiers.append(attr.describe_signature(modifiers)) + if len(modifiers) != 0 and len(self.attrs) != 0: + modifiers.append(addnodes.desc_sig_space()) + 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 ) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index f62a8d06a..26a2f2cd2 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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 diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index e751ae9bb..027417dd6 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -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 diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 16a71d9fe..dcae320e3 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -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'}) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 5f4c19171..e344ce45b 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -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(): |