diff --git a/CHANGES b/CHANGES index b6d77a409..8f08f1428 100644 --- a/CHANGES +++ b/CHANGES @@ -167,6 +167,23 @@ Bugs fixed Thanks to Takeshi Komiya. * PR#265: Fix could not capture caption of graphviz node by xref. Thanks to Takeshi Komiya. +* PR#263, #1013, #1103: Rewrite of C++ domain. Thanks to Jakob Lykke Andersen. + + * Hyperlinks to all found nested names and template arguments (#1103). + * Support for function types everywhere, e.g., in + std::function (#1013). + * Support for virtual functions. + * Changed interpretation of function arguments to following standard + prototype declarations, i.e., void f(arg) means that arg is the type of the + argument, instead of it being the name. + * Updated tests. + * Updated documentation with elaborate description of what declarations are + supported and how the namespace declarations influence declaration and + cross-reference lookup. + * Index names may be different now. Elements are indexed by their fully + qualified name. It should be rather easy to change this behaviour and + potentially index by namespaces/classes as well. + Documentation ------------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 199c8ea4b..23bd469fd 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -9,20 +9,24 @@ :license: BSD, see LICENSE for details. See http://www.nongnu.org/hcb/ for the grammar. - See http://mentorembedded.github.io/cxx-abi/abi.html#mangling for the inspiration for the id generation. + See http://mentorembedded.github.io/cxx-abi/abi.html#mangling for the + inspiration for the id generation. common grammar things: simple-declaration - -> attribute-specifier-seq[opt] decl-specifier-seq[opt] init-declarator-list[opt] ; + -> attribute-specifier-seq[opt] decl-specifier-seq[opt] + init-declarator-list[opt] ; # Drop the semi-colon. For now: drop the attributes (TODO). # Use at most 1 init-declerator. -> decl-specifier-seq init-declerator -> decl-specifier-seq declerator initializer decl-specifier -> - storage-class-specifier -> "static" (only for member_object and function_object) + storage-class-specifier -> "static" (only for member_object and + function_object) | type-specifier -> trailing-type-specifier - | function-specifier -> "inline" | "virtual" | "explicit" (only for function_object) + | function-specifier -> "inline" | "virtual" | "explicit" (only + for function_object) | "friend" (only for function_object) | "constexpr" (only for member_object and function_object) trailing-type-specifier -> @@ -30,10 +34,13 @@ | elaborated-type-specifier | typename-specifier | cv-qualifier -> "const" | "volatile" - stricter grammar for decl-specifier-seq (with everything, each object uses a subset): - visibility storage-class-specifier function-specifier "friend" "constexpr" "volatile" "const" trailing-type-specifier + stricter grammar for decl-specifier-seq (with everything, each object + uses a subset): + visibility storage-class-specifier function-specifier "friend" + "constexpr" "volatile" "const" trailing-type-specifier # where trailing-type-specifier can no be cv-qualifier - # Inside e.g., template paramters a strict subset is used (see type-specifier-seq) + # Inside e.g., template paramters a strict subset is used + # (see type-specifier-seq) trailing-type-specifier -> simple-type-specifier -> ::[opt] nested-name-specifier[opt] type-name @@ -41,12 +48,15 @@ | "char" | "bool" | ect. | decltype-specifier | elaborated-type-specifier -> - class-key attribute-specifier-seq[opt] ::[opt] nested-name-specifier[opt] identifier - | class-key ::[opt] nested-name-specifier[opt] template[opt] simple-template-id + class-key attribute-specifier-seq[opt] ::[opt] + nested-name-specifier[opt] identifier + | class-key ::[opt] nested-name-specifier[opt] template[opt] + simple-template-id | "enum" ::[opt] nested-name-specifier[opt] identifier | typename-specifier -> "typename" ::[opt] nested-name-specifier identifier - | "typename" ::[opt] nested-name-specifier template[opt] simple-template-id + | "typename" ::[opt] nested-name-specifier template[opt] + simple-template-id class-key -> "class" | "struct" | "union" type-name ->* identifier | simple-template-id # ignoring attributes and decltype, and then some left-factoring @@ -70,7 +80,8 @@ declerator -> ptr-declerator - | noptr-declarator parameters-and-qualifiers trailing-return-type (TODO: for now we don't support it) + | noptr-declarator parameters-and-qualifiers trailing-return-type + (TODO: for now we don't support it) ptr-declerator -> noptr-declerator | ptr-operator ptr-declarator @@ -79,11 +90,15 @@ "..."[opt] id-expression | rest-of-trailing | noptr-declerator parameters-and-qualifiers - | noptr-declarator "[" constant-expression[opt] "]" attribute-specifier-seq[opt] + | noptr-declarator "[" constant-expression[opt] "]" + attribute-specifier-seq[opt] | "(" ptr-declarator ")" # TODO: not implemented yet - # function_object must use a parameters-and-qualifiers, the others may use it (e.g., function poitners) + # function_object must use a parameters-and-qualifiers, the others may + # use it (e.g., function poitners) parameters-and-qualifiers -> - "(" parameter-clause ")" attribute-specifier-seq[opt] cv-qualifier-seq[opt] ref-qualifier[opt] exception-specification[opt] + "(" parameter-clause ")" attribute-specifier-seq[opt] + cv-qualifier-seq[opt] ref-qualifier[opt] + exception-specification[opt] ref-qualifier -> "&" | "&&" exception-specification -> "noexcept" ("(" constant-expression ")")[opt] @@ -91,18 +106,22 @@ # TODO: we don't implement attributes # member functions can have initializers, but we fold them into here memberFunctionInit -> "=" "0" - # (note: only "0" is allowed as the value, according to the standard, right?) + # (note: only "0" is allowed as the value, according to the standard, + # right?) - We additionally add the possibility for specifying the visibility as the first thing. + We additionally add the possibility for specifying the visibility as the + first thing. type_object: goal: - either a single type (e.g., "MyClass:Something_T" or a typedef-like thing - (e.g. "Something Something_T" or "int I_arr[]" - grammar, single type: based on a type in a function parameter, but without a name: + either a single type (e.g., "MyClass:Something_T" or a typedef-like + thing (e.g. "Something Something_T" or "int I_arr[]" + grammar, single type: based on a type in a function parameter, but + without a name: parameter-declaration - -> attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] + -> attribute-specifier-seq[opt] decl-specifier-seq + abstract-declarator[opt] # Drop the attributes -> decl-specifier-seq abstract-declarator[opt] grammar, typedef-like: no initilizer @@ -110,7 +129,8 @@ member_object: - goal: as a type_object which must have a declerator, and optionally with a initializer + goal: as a type_object which must have a declerator, and optionally + with a initializer grammar: decl-specifier-seq declerator initializer @@ -155,90 +175,106 @@ _operator_re = re.compile(r'''(?x) ''') _id_prefix = '_CPP' -_id_fundamental = { # not all of these are actually parsed as fundamental types, TODO: do that - 'void' : 'v', - 'bool' : 'b', - 'char' : 'c', - 'signed char' : 'a', - 'unsigned char' : 'h', - 'wchar_t' : 'w', - 'char32_t' : 'Di', - 'char16_t' : 'Ds', - 'short' : 's', 'short int' : 's', 'signed short' : 's', 'signed short int' : 's', - 'unsigned short' : 't', 'unsigned short int' : 't', - 'int' : 'i', 'signed' : 'i', 'signed int' : 'i', - 'unsigned' : 'j', 'unsigned int' : 'j', - 'long' : 'l', 'long int' : 'l', 'signed long' : 'l', 'signed long int' : 'l', - 'unsigned long' : 'm', 'unsigned long int' : 'm', - 'long long' : 'x', 'long long int' : 'x', 'signed long long' : 'x', 'signed long long int' : 'x', - 'unsigned long long' : 'y', 'unsigned long long int' : 'y', - 'float' : 'f', - 'double' : 'd', - 'long double' : 'e', - 'auto' : 'Da', - 'decltype(auto)' : 'Dc', - 'std::nullptr_t' : 'Dn' +_id_fundamental = { + # not all of these are actually parsed as fundamental types, TODO: do that + 'void': 'v', + 'bool': 'b', + 'char': 'c', + 'signed char': 'a', + 'unsigned char': 'h', + 'wchar_t': 'w', + 'char32_t': 'Di', + 'char16_t': 'Ds', + 'short': 's', + 'short int': 's', + 'signed short': 's', + 'signed short int': 's', + 'unsigned short': 't', + 'unsigned short int': 't', + 'int': 'i', + 'signed': 'i', + 'signed int': 'i', + 'unsigned': 'j', + 'unsigned int': 'j', + 'long': 'l', + 'long int': 'l', + 'signed long': 'l', + 'signed long int': 'l', + 'unsigned long': 'm', + 'unsigned long int': 'm', + 'long long': 'x', + 'long long int': 'x', + 'signed long long': 'x', + 'signed long long int': 'x', + 'unsigned long long': 'y', + 'unsigned long long int': 'y', + 'float': 'f', + 'double': 'd', + 'long double': 'e', + 'auto': 'Da', + 'decltype(auto)': 'Dc', + 'std::nullptr_t': 'Dn' } _id_operator = { - 'new' : 'nw', - 'new[]' : 'na', - 'delete' : 'dl', - 'delete[]' : 'da', + 'new': 'nw', + 'new[]': 'na', + 'delete': 'dl', + 'delete[]': 'da', # the arguments will make the difference between unary and binary - #'+(unary)' : 'ps', + # '+(unary)' : 'ps', #'-(unary)' : 'ng', #'&(unary)' : 'ad', #'*(unary)' : 'de', - '~' : 'co', - '+' : 'pl', - '-' : 'mi', - '*' : 'ml', - '/' : 'dv', - '%' : 'rm', - '&' : 'an', - '|' : 'or', - '^' : 'eo', - '=' : 'aS', - '+=' : 'pL', - '-=' : 'mI', - '*=' : 'mL', - '/=' : 'dV', - '%=' : 'rM', - '&=' : 'aN', - '|=' : 'oR', - '^=' : 'eO', - '<<' : 'ls', - '>>' : 'rs', - '<<=' : 'lS', - '>>=' : 'rS', - '==' : 'eq', - '!=' : 'ne', - '<' : 'lt', - '>' : 'gt', - '<=' : 'le', - '>=' : 'ge', - '!' : 'nt', - '&&' : 'aa', - '||' : 'oo', - '++' : 'pp', - '--' : 'mm', - ',' : 'cm', - '->*' : 'pm', - '->' : 'pt', - '()' : 'cl', - '[]' : 'ix' + '~': 'co', + '+': 'pl', + '-': 'mi', + '*': 'ml', + '/': 'dv', + '%': 'rm', + '&': 'an', + '|': 'or', + '^': 'eo', + '=': 'aS', + '+=': 'pL', + '-=': 'mI', + '*=': 'mL', + '/=': 'dV', + '%=': 'rM', + '&=': 'aN', + '|=': 'oR', + '^=': 'eO', + '<<': 'ls', + '>>': 'rs', + '<<=': 'lS', + '>>=': 'rS', + '==': 'eq', + '!=': 'ne', + '<': 'lt', + '>': 'gt', + '<=': 'le', + '>=': 'ge', + '!': 'nt', + '&&': 'aa', + '||': 'oo', + '++': 'pp', + '--': 'mm', + ',': 'cm', + '->*': 'pm', + '->': 'pt', + '()': 'cl', + '[]': 'ix' } -class DefinitionError(UnicodeMixin, Exception): +class DefinitionError(UnicodeMixin, Exception): def __init__(self, description): self.description = description def __unicode__(self): return self.description -class ASTBase(UnicodeMixin): +class ASTBase(UnicodeMixin): def __eq__(self, other): if type(self) is not type(other): return False @@ -280,29 +316,32 @@ class ASTBase(UnicodeMixin): def __repr__(self): return '<%s %s>' % (self.__class__.__name__, self) - + + def _verify_description_mode(mode): - if not mode in {'lastIsName', 'noneIsName', 'markType', 'param'}: + if not mode in ('lastIsName', 'noneIsName', 'markType', 'param'): raise Exception("Description mode '%s' is invalid." % mode) + class ASTOperatorBuildIn(ASTBase): - def __init__(self, op): self.op = op - + def get_id(self): if not self.op in _id_operator: - raise Exception('Internal error: Build-in operator "%s" can not be mapped to an id.' % self.op) + raise Exception('Internal error: Build-in operator "%s" can not ' + 'be mapped to an id.' % self.op) return _id_operator[self.op] - + def __unicode__(self): - if self.op in {'new', 'new[]', 'delete', 'delete[]'}: + if self.op in ('new', 'new[]', 'delete', 'delete[]'): return u'operator ' + self.op - else: return u'operator' + self.op - + else: + return u'operator' + self.op + def get_name_no_template(self): return text_type(self) - + def describe_signature(self, signode, mode, env, prefix): _verify_description_mode(mode) identifier = text_type(self) @@ -310,21 +349,21 @@ class ASTOperatorBuildIn(ASTBase): signode += addnodes.desc_name(identifier, identifier) else: signode += addnodes.desc_addname(identifier, identifier) - + + class ASTOperatorType(ASTBase): - def __init__(self, type): self.type = type - + def __unicode__(self): return u''.join(['operator ', text_type(self.type)]) - + def get_id(self): return u'cv' + self.type.get_id() - + def get_name_no_template(self): return text_type(self) - + def describe_signature(self, signode, mode, env, prefix): _verify_description_mode(mode) identifier = text_type(self) @@ -332,29 +371,30 @@ class ASTOperatorType(ASTBase): signode += addnodes.desc_name(identifier, identifier) else: signode += addnodes.desc_addname(identifier, identifier) - + + class ASTTemplateArgConstant(ASTBase): - def __init__(self, value): self.value = value - + def __unicode__(self): return text_type(self.value) - + def get_id(self): - # TODO: doing this properly needs parsing of expressions, let's just juse it verbatim for now - return u'X' + text_type(self) + u'E' - + # TODO: doing this properly needs parsing of expressions, let's just + # juse it verbatim for now + return u'X' + text_type(self) + u'E' + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) signode += nodes.Text(text_type(self)) -class ASTNestedNameElement(ASTBase): +class ASTNestedNameElement(ASTBase): def __init__(self, identifier, templateArgs): self.identifier = identifier self.templateArgs = templateArgs - + def get_id(self): res = [] if self.identifier == "std": @@ -368,7 +408,7 @@ class ASTNestedNameElement(ASTBase): res.append(a.get_id()) res.append('E') return u''.join(res) - + def __unicode__(self): res = [] res.append(self.identifier) @@ -376,15 +416,16 @@ class ASTNestedNameElement(ASTBase): res.append('<') first = True for a in self.templateArgs: - if not first: res.append(', ') + if not first: + res.append(', ') first = False res.append(text_type(a)) res.append('>') return u''.join(res) - + def get_name_no_template(self): return text_type(self.identifier) - + def describe_signature(self, signode, mode, env, prefix): _verify_description_mode(mode) if mode == 'markType': @@ -392,7 +433,7 @@ class ASTNestedNameElement(ASTBase): pnode = addnodes.pending_xref( '', refdomain='cpp', reftype='type', reftarget=targetText, modname=None, classname=None) - if env: # during testing we don't have an env, do we? + if env: # during testing we don't have an env, do we? pnode['cpp:parent'] = env.temp_data.get('cpp:parent') pnode += nodes.Text(text_type(self.identifier)) signode += pnode @@ -405,21 +446,23 @@ class ASTNestedNameElement(ASTBase): signode += nodes.Text('<') first = True for a in self.templateArgs: - if not first: signode += nodes.Text(', ') + if not first: + signode += nodes.Text(', ') first = False a.describe_signature(signode, 'markType', env) signode += nodes.Text('>') + class ASTNestedName(ASTBase): - def __init__(self, names): - """Use an empty string as the first name if it should start with '::' """ + """Use an empty string as the first name if it should start with '::' + """ self.names = names - + @property def name(self): return self - + def get_id(self): res = [] if len(self.names) > 1: @@ -429,29 +472,31 @@ class ASTNestedName(ASTBase): if len(self.names) > 1: res.append('E') return u''.join(res) - + def get_name_no_last_template(self): res = u'::'.join([text_type(n) for n in self.names[:-1]]) - if len(self.names) > 1: res += '::' + if len(self.names) > 1: + res += '::' res += self.names[-1].get_name_no_template() return res - + def prefix_nested_name(self, prefix): if self.names[0] == '': - return self # it's defined at global namespace, don't tuch it + return self # it's defined at global namespace, don't tuch it assert isinstance(prefix, ASTNestedName) names = prefix.names[:] names.extend(self.names) return ASTNestedName(names) - + def __unicode__(self): return u'::'.join([text_type(n) for n in self.names]) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) if mode == 'lastIsName': addname = u'::'.join([text_type(n) for n in self.names[:-1]]) - if len(self.names) > 1: addname += u'::' + if len(self.names) > 1: + addname += u'::' name = text_type(self.names[-1]) signode += addnodes.desc_addname(addname, addname) self.names[-1].describe_signature(signode, mode, env, '') @@ -462,8 +507,9 @@ class ASTNestedName(ASTBase): name = text_type(self) signode += nodes.emphasis(name, name) elif mode == 'markType': - # each element should be a pending xref targeting the complete prefix - # however, only the identifier part should be a link, such that template args can be a link as well + # each element should be a pending xref targeting the complete + # prefix. however, only the identifier part should be a link, such + # that template args can be a link as well. prefix = '' first = True for name in self.names: @@ -477,35 +523,38 @@ class ASTNestedName(ASTBase): else: raise Exception('Unknown description mode: %s' % mode) + class ASTTrailingTypeSpecFundamental(ASTBase): - def __init__(self, name): self.name = name - + def __unicode__(self): return self.name - + def get_id(self): if not self.name in _id_fundamental: - raise Exception('Semi-internal error: Fundamental type "%s" can not be mapped to an id. Is it a true fundamental type? If not so, the parser should have rejected it.' % self.name) + raise Exception( + 'Semi-internal error: Fundamental type "%s" can not be mapped ' + 'to an id. Is it a true fundamental type? If not so, the ' + 'parser should have rejected it.' % self.name) return _id_fundamental[self.name] - + def describe_signature(self, signode, mode, env): signode += nodes.Text(text_type(self.name)) - + + class ASTTrailingTypeSpecName(ASTBase): - def __init__(self, prefix, nestedName): self.prefix = prefix self.nestedName = nestedName - + @property def name(self): return self.nestedName - + def get_id(self): return self.nestedName.get_id() - + def __unicode__(self): res = [] if self.prefix: @@ -513,35 +562,42 @@ class ASTTrailingTypeSpecName(ASTBase): res.append(' ') res.append(text_type(self.nestedName)) return u''.join(res) - + def describe_signature(self, signode, mode, env): if self.prefix: signode += addnodes.desc_annotation(self.prefix, self.prefix) signode += nodes.Text(' ') self.nestedName.describe_signature(signode, mode, env) + class ASTFunctinoParameter(ASTBase): - def __init__(self, arg, ellipsis=False): self.arg = arg self.ellipsis = ellipsis - + def get_id(self): - if self.ellipsis: return 'z' - else: return self.arg.get_id() - + if self.ellipsis: + return 'z' + else: + return self.arg.get_id() + def __unicode__(self): - if self.ellipsis: return '...' - else: return text_type(self.arg) - + if self.ellipsis: + return '...' + else: + return text_type(self.arg) + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) - if self.ellipsis: signode += nodes.Text('...') - else: self.arg.describe_signature(signode, mode, env) + if self.ellipsis: + signode += nodes.Text('...') + else: + self.arg.describe_signature(signode, mode, env) + class ASTParametersQualifiers(ASTBase): - - def __init__(self, args, volatile, const, refQual, exceptionSpec, override, final, initializer): + def __init__(self, args, volatile, const, refQual, exceptionSpec, override, + final, initializer): self.args = args self.volatile = volatile self.const = const @@ -550,69 +606,92 @@ class ASTParametersQualifiers(ASTBase): self.override = override self.final = final self.initializer = initializer - + def get_modifiers_id(self): res = [] - if self.volatile: res.append('V') - if self.const: res.append('K') - if self.refQual == '&&': res.append('O') - elif self.refQual == '&': res.append('R') + if self.volatile: + res.append('V') + if self.const: + res.append('K') + if self.refQual == '&&': + res.append('O') + elif self.refQual == '&': + res.append('R') return u''.join(res) - + def get_param_id(self): - if len(self.args) == 0: return 'v' - else: return u''.join(a.get_id() for a in self.args) - + if len(self.args) == 0: + return 'v' + else: + return u''.join(a.get_id() for a in self.args) + def __unicode__(self): res = [] res.append('(') first = True for a in self.args: - if not first: res.append(', ') + if not first: + res.append(', ') first = False res.append(text_type(a)) res.append(')') - if self.volatile: res.append(' volatile') - if self.const: res.append(' const') + if self.volatile: + res.append(' volatile') + if self.const: + res.append(' const') if self.refQual: res.append(' ') res.append(self.refQual) if self.exceptionSpec: res.append(' ') res.append(text_type(self.exceptionSpec)) - if self.final: res.append(' final') - if self.override: res.append(' override') + if self.final: + res.append(' final') + if self.override: + res.append(' override') if self.initializer: res.append(' = ') res.append(self.initializer) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) paramlist = addnodes.desc_parameterlist() for arg in self.args: param = addnodes.desc_parameter('', '', noemph=True) - if mode == 'lastIsName': # i.e., outer-function params + if mode == 'lastIsName': # i.e., outer-function params arg.describe_signature(param, 'param', env) - else:arg.describe_signature(param, 'markType', env) + else: + arg.describe_signature(param, 'markType', env) paramlist += param signode += paramlist + def _add_anno(signode, text): signode += nodes.Text(' ') signode += addnodes.desc_annotation(text, text) + def _add_text(signode, text): signode += nodes.Text(' ' + text) - if self.volatile: _add_anno(signode, 'volatile') - if self.const: _add_anno(signode, 'const') - if self.refQual: _add_text(signode, self.refQual) - if self.exceptionSpec: _add_anno(signode, text_type(self.exceptionSpec)) - if self.final: _add_anno(signode, 'final') - if self.override: _add_anno(signode, 'override') - if self.initializer: _add_text(signode, '= ' + text_type(self.initializer)) + + if self.volatile: + _add_anno(signode, 'volatile') + if self.const: + _add_anno(signode, 'const') + if self.refQual: + _add_text(signode, self.refQual) + if self.exceptionSpec: + _add_anno(signode, text_type(self.exceptionSpec)) + if self.final: + _add_anno(signode, 'final') + if self.override: + _add_anno(signode, 'override') + if self.initializer: + _add_text(signode, '= ' + text_type(self.initializer)) + class ASTDeclSpecs(ASTBase): - - def __init__(self, outer, visibility, storage, inline, virtual, explicit, constexpr, volatile, const, trailing): + def __init__(self, outer, visibility, storage, inline, virtual, explicit, + constexpr, volatile, const, trailing): self.outer = outer self.visibility = visibility self.storage = storage @@ -623,247 +702,293 @@ class ASTDeclSpecs(ASTBase): self.volatile = volatile self.const = const self.trailingTypeSpec = trailing - + @property def name(self): return self.trailingTypeSpec.name - + def get_id(self): res = [] - if self.volatile: res.append('V') - if self.const: res.append('K') + if self.volatile: + res.append('V') + if self.const: + res.append('K') res.append(self.trailingTypeSpec.get_id()) return u''.join(res) - + def _print_visibility(self): - return self.visibility and not (self.outer in {'type', 'member', 'function'} and self.visibility == 'public') - + return (self.visibility and + not ( + self.outer in ('type', 'member', 'function') and + self.visibility == 'public')) + def __unicode__(self): res = [] - if self._print_visibility(): res.append(self.visibility) - if self.storage: res.append(self.storage) - if self.inline: res.append('inline') - if self.virtual: res.append('virtual') - if self.explicit: res.append('explicit') - if self.constexpr: res.append('constexpr') - if self.volatile: res.append('volatile') - if self.const: res.append('const') - if self.trailingTypeSpec: res.append(text_type(self.trailingTypeSpec)) + if self._print_visibility(): + res.append(self.visibility) + if self.storage: + res.append(self.storage) + if self.inline: + res.append('inline') + if self.virtual: + res.append('virtual') + if self.explicit: + res.append('explicit') + if self.constexpr: + res.append('constexpr') + if self.volatile: + res.append('volatile') + if self.const: + res.append('const') + if self.trailingTypeSpec: + res.append(text_type(self.trailingTypeSpec)) return u' '.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) modifiers = [] + def _add(modifiers, text): if len(modifiers) > 0: modifiers.append(nodes.Text(' ')) modifiers.append(addnodes.desc_annotation(text, text)) - if self._print_visibility(): _add(modifiers, self.visibility) - if self.storage: _add(modifiers, self.storage) - if self.inline: _add(modifiers, 'inline') - if self.virtual: _add(modifiers, 'virtual') - if self.explicit: _add(modifiers, 'explicit') - if self.constexpr: _add(modifiers, 'constexpr') - if self.volatile: _add(modifiers, 'volatile') - if self.const: _add(modifiers, 'const') - for m in modifiers: signode += m + + if self._print_visibility(): + _add(modifiers, self.visibility) + if self.storage: + _add(modifiers, self.storage) + if self.inline: + _add(modifiers, 'inline') + if self.virtual: + _add(modifiers, 'virtual') + if self.explicit: + _add(modifiers, 'explicit') + if self.constexpr: + _add(modifiers, 'constexpr') + if self.volatile: + _add(modifiers, 'volatile') + if self.const: + _add(modifiers, 'const') + for m in modifiers: + signode += m if self.trailingTypeSpec: if len(modifiers) > 0: signode += nodes.Text(' ') self.trailingTypeSpec.describe_signature(signode, mode, env) - + + class ASTPtrOpPtr(ASTBase): - def __init__(self, volatile, const): self.volatile = volatile self.const = const - + def __unicode__(self): res = ['*'] - if self.volatile: res.append('volatile ') - if self.const: res.append('const ') + if self.volatile: + res.append('volatile ') + if self.const: + res.append('const ') return u''.join(res) - + def get_id(self): res = ['P'] - if self.volatile: res.append('V') - if self.const: res.append('C') + if self.volatile: + res.append('V') + if self.const: + res.append('C') return u''.join(res) - + + class ASTPtrOpRef(ASTBase): - def __unicode__(self): return '&' - + def get_id(self): return 'R' - + + class ASTPtrOpParamPack(ASTBase): - def __unicode__(self): return '...' - + def get_id(self): return 'Dp' - + + class ASTArray(ASTBase): - def __init__(self, size): self.size = size - + def __unicode__(self): return u''.join(['[', text_type(self.size), ']']) - + def get_id(self): # TODO: this should maybe be done differently return u'A' + text_type(self.size) + u'_' - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) signode += nodes.Text(text_type(self)) - + + class ASTDeclerator(ASTBase): - def __init__(self, ptrOps, declId, suffixOps): self.ptrOps = ptrOps self.declId = declId self.suffixOps = suffixOps - + @property def name(self): return self.declId - - def get_modifiers_id(self): # only the modifiers for a function, e.g., cv-qualifiers + + def get_modifiers_id(self): # only the modifiers for a function, e.g., + # cv-qualifiers for op in self.suffixOps: if isinstance(op, ASTParametersQualifiers): return op.get_modifiers_id() - raise Exception("This should only be called on a function: %s" % text_type(self)) - - def get_param_id(self): # only the parameters (if any) + raise Exception( + "This should only be called on a function: %s" % text_type(self)) + + def get_param_id(self): # only the parameters (if any) for op in self.suffixOps: if isinstance(op, ASTParametersQualifiers): return op.get_param_id() return '' - - def get_ptr_suffix_id(self): # only the ptr ops and array specifiers - return u''.join(a.get_id() for a in self.ptrOps + self.suffixOps if not isinstance(a, ASTParametersQualifiers)) - + + def get_ptr_suffix_id(self): # only the ptr ops and array specifiers + return u''.join( + a.get_id() + for a in self.ptrOps + self.suffixOps + if not isinstance(a, ASTParametersQualifiers)) + def require_start_space(self): - if len(self.ptrOps) > 0 and isinstance(self.ptrOps[-1], ASTPtrOpParamPack): return False - else: return self.declId != None - + if (len(self.ptrOps) > 0 and + isinstance(self.ptrOps[-1], ASTPtrOpParamPack)): + return False + else: + return self.declId != None + def __unicode__(self): res = [] for op in self.ptrOps: res.append(text_type(op)) - if isinstance(op, ASTPtrOpParamPack) and self.declId: res.append(' ') + if isinstance(op, ASTPtrOpParamPack) and self.declId: + res.append(' ') if self.declId: res.append(text_type(self.declId)) - for op in self.suffixOps: res.append(text_type(op)) + for op in self.suffixOps: + res.append(text_type(op)) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) for op in self.ptrOps: signode += nodes.Text(text_type(op)) - if isinstance(op, ASTPtrOpParamPack) and self.declId: signode += nodes.Text(' ') - if self.declId: self.declId.describe_signature(signode, mode, env) - for op in self.suffixOps: op.describe_signature(signode, mode, env) - + if isinstance(op, ASTPtrOpParamPack) and self.declId: + signode += nodes.Text(' ') + if self.declId: + self.declId.describe_signature(signode, mode, env) + for op in self.suffixOps: + op.describe_signature(signode, mode, env) + + class ASTInitializer(ASTBase): - def __init__(self, value): self.value = value - + def __unicode__(self): return u''.join([' = ', text_type(self.value)]) - + def describe_signature(self, signode, mode): _verify_description_mode(mode) signode += nodes.Text(text_type(self)) + class ASTType(ASTBase): - def __init__(self, declSpecs, decl): self.declSpecs = declSpecs self.decl = decl self.objectType = None - + @property def name(self): name = self.decl.name - if not name: name = self.declSpecs.name + if not name: + name = self.declSpecs.name return name - + def get_id(self): res = [] - if self.objectType: # needs the name + if self.objectType: # needs the name res.append(_id_prefix) - if self.objectType == 'function': # also modifiers + if self.objectType == 'function': # also modifiers res.append(self.decl.get_modifiers_id()) res.append(self.prefixedName.get_id()) res.append(self.decl.get_param_id()) - elif self.objectType == 'type': # just the name + elif self.objectType == 'type': # just the name res.append(self.prefixedName.get_id()) else: print(self.objectType) assert False - else: # only type encoding + else: # only type encoding res.append(self.decl.get_ptr_suffix_id()) res.append(self.declSpecs.get_id()) res.append(self.decl.get_param_id()) return u''.join(res) - + def __unicode__(self): res = [] - declSpecs = text_type(self.declSpecs) + declSpecs = text_type(self.declSpecs) res.append(declSpecs) if self.decl.require_start_space() and len(declSpecs) > 0: res.append(u' ') res.append(text_type(self.decl)) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) self.declSpecs.describe_signature(signode, 'markType', env) - if self.decl.require_start_space() and len(text_type(self.declSpecs)) > 0: + if (self.decl.require_start_space() and + len(text_type(self.declSpecs)) > 0): signode += nodes.Text(' ') self.decl.describe_signature(signode, mode, env) - + + class ASTTypeWithInit(ASTBase): - def __init__(self, type, init): self.objectType = None self.type = type self.init = init - + @property def name(self): return self.type.name - + def get_id(self): if self.objectType == 'member': return _id_prefix + self.prefixedName.get_id() else: return self.type.get_id() - + def __unicode__(self): res = [] res.append(text_type(self.type)) - if self.init: res.append(text_type(self.init)) + if self.init: + res.append(text_type(self.init)) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) self.type.describe_signature(signode, mode, env) - if self.init: self.init.describe_signature(signode, mode) + if self.init: + self.init.describe_signature(signode, mode) + class ASTBaseClass(ASTBase): - def __init__(self, name, visibility): self.name = name self.visibility = visibility - + def __unicode__(self): res = [] if self.visibility != 'private': @@ -871,23 +996,24 @@ class ASTBaseClass(ASTBase): res.append(' ') res.append(text_type(self.name)) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) if self.visibility != 'private': - signode += addnodes.desc_annotation(self.visibility, self.visibility) + signode += addnodes.desc_annotation( + self.visibility, self.visibility) signode += nodes.Text(' ') self.name.describe_signature(signode, mode, env) + class ASTClass(ASTBase): - def __init__(self, name, bases): self.name = name self.bases = bases - + def get_id(self): return _id_prefix + self.prefixedName.get_id() - + def __unicode__(self): res = [] res.append(text_type(self.name)) @@ -895,30 +1021,32 @@ class ASTClass(ASTBase): res.append(' : ') first = True for b in self.bases: - if not first: res.append(', ') + if not first: + res.append(', ') first = False res.append(text_type(b)) return u''.join(res) - + def describe_signature(self, signode, mode, env): _verify_description_mode(mode) self.name.describe_signature(signode, mode, env) if len(self.bases) > 0: signode += nodes.Text(' : ') for b in self.bases: - b.describe_signature(signode, mode, env) + b.describe_signature(signode, mode, env) signode += nodes.Text(', ') signode.pop() + class DefinitionParser(object): - # those without signedness and size modifiers # see http://en.cppreference.com/w/cpp/language/types - _simple_fundemental_types = { - 'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int', 'float', 'double', 'auto' - } - - _prefix_keys = {'class', 'struct', 'union', 'typename'} + _simple_fundemental_types = ( + 'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int', + 'float', 'double', 'auto' + ) + + _prefix_keys = ('class', 'struct', 'union', 'typename') def __init__(self, definition): self.definition = definition.strip() @@ -928,8 +1056,9 @@ class DefinitionParser(object): self._previous_state = (0, None) def fail(self, msg): - indicator = '-'*self.pos + '^' - raise DefinitionError('Invalid definition: %s [error at %d]\n %s\n %s' % + indicator = '-' * self.pos + '^' + raise DefinitionError( + 'Invalid definition: %s [error at %d]\n %s\n %s' % (msg, self.pos, self.definition, indicator)) def match(self, regex): @@ -987,16 +1116,16 @@ class DefinitionParser(object): def assert_end(self): self.skip_ws() if not self.eof: - self.fail('expected end of definition, got %r' % + self.fail('expected end of definition, got %r' % self.definition[self.pos:]) - + def _parse_operator(self): self.skip_ws() # adapted from the old code # thank god, a regular operator definition if self.match(_operator_re): return ASTOperatorBuildIn(self.matched_text) - + # new/delete operator? for op in 'new', 'delete': if not self.skip_word(op): @@ -1008,17 +1137,18 @@ class DefinitionParser(object): self.fail('Expected "]" after "operator ' + op + '["') op += '[]' return ASTOperatorBuildIn(op) - + # oh well, looks like a cast operator definition. # In that case, eat another type. type = self._parse_type() return ASTOperatorType(type) - + def _parse_nested_name(self): names = [] - + self.skip_ws() - if self.skip_string('::'): names.append(u'') + if self.skip_string('::'): + names.append(u'') while 1: self.skip_ws() # TODO: parse the "template" keyword @@ -1034,7 +1164,7 @@ class DefinitionParser(object): if self.skip_string('<'): templateArgs = [] while 1: - pos = self.pos + pos = self.pos try: type = self._parse_type(allowParams=True) templateArgs.append(type) @@ -1047,49 +1177,70 @@ class DefinitionParser(object): value = self.matched_text else: while not self.eof: - if len(symbols) == 0 and self.current_char in {',', '>'}: break - # TODO: actually implement nice handling of quotes, braces, brackets, parens, and whatever + if (len(symbols) == 0 and + self.current_char in ( + ',', '>')): + break + # TODO: actually implement nice handling + # of quotes, braces, brackets, parens, and + # whatever self.pos += 1 if self.eof: self.pos = startPos - self.fail('Could not find end of constant template argument.') - value = self.definition[startPos:self.pos].strip() + self.fail( + 'Could not find end of constant ' + 'template argument.') + value = self.definition[ + startPos:self.pos].strip() templateArgs.append(ASTTemplateArgConstant(value)) self.skip_ws() - if self.skip_string('>'): break - elif self.skip_string(','): continue + if self.skip_string('>'): + break + elif self.skip_string(','): + continue else: - self.fail('Expected ">" or "," in template argument list.') + self.fail('Expected ">" or "," in template ' + 'argument list.') names.append(ASTNestedNameElement(identifier, templateArgs)) - + self.skip_ws() - if not self.skip_string('::'): break + if not self.skip_string('::'): + break return ASTNestedName(names) - - def _parse_trailing_type_spec(self): + + def _parse_trailing_type_spec(self): # fundemental types self.skip_ws() for t in self._simple_fundemental_types: - if self.skip_word(t): return ASTTrailingTypeSpecFundamental(t) - + if self.skip_word(t): + return ASTTrailingTypeSpecFundamental(t) + # TODO: this could/should be more strict elements = [] self.skip_ws() - if self.skip_word_and_ws('signed'): elements.append('signed') - elif self.skip_word_and_ws('unsigned'): elements.append('unsigned') + if self.skip_word_and_ws('signed'): + elements.append('signed') + elif self.skip_word_and_ws('unsigned'): + elements.append('unsigned') while 1: - if self.skip_word_and_ws('short'): elements.append('short') - elif self.skip_word_and_ws('long'): elements.append('long') - else: break - if self.skip_word_and_ws('int'): elements.append('int') - elif self.skip_word_and_ws('double'): elements.append('double') - if len(elements) > 0: return ASTTrailingTypeSpecFundamental(u' '.join(elements)) - + if self.skip_word_and_ws('short'): + elements.append('short') + elif self.skip_word_and_ws('long'): + elements.append('long') + else: + break + if self.skip_word_and_ws('int'): + elements.append('int') + elif self.skip_word_and_ws('double'): + elements.append('double') + if len(elements) > 0: + return ASTTrailingTypeSpecFundamental(u' '.join(elements)) + # decltype self.skip_ws() if self.skip_word_and_ws('decltype'): self.fail('"decltype(.)" in trailing_type_spec not implemented') - + # prefixed prefix = None self.skip_ws() @@ -1097,16 +1248,17 @@ class DefinitionParser(object): if self.skip_word_and_ws(k): prefix = k break - + nestedName = self._parse_nested_name() return ASTTrailingTypeSpecName(prefix, nestedName) - + def _parse_parameters_and_qualifiers(self, paramMode): self.skip_ws() if not self.skip_string('('): if paramMode == 'function': self.fail('Expecting "(" in parameters_and_qualifiers.') - else: return None + else: + return None args = [] self.skip_ws() if not self.skip_string(')'): @@ -1116,7 +1268,8 @@ class DefinitionParser(object): args.append(ASTFunctinoParameter(None, True)) self.skip_ws() if not self.skip_string(')'): - self.fail('Expected ")" after "..." in parameters_and_qualifiers.') + self.fail('Expected ")" after "..." in ' + 'parameters_and_qualifiers.') break if paramMode == 'function': arg = self._parse_type_with_init(named='maybe') @@ -1124,25 +1277,33 @@ class DefinitionParser(object): arg = self._parse_type() # TODO: parse default parameters args.append(ASTFunctinoParameter(arg)) - + self.skip_ws() - if self.skip_string(','): continue - elif self.skip_string(')'): break - else: self.fail('Expecting "," or ")" in parameters_and_qualifiers, got "%s".' % self.current_char) - + if self.skip_string(','): + continue + elif self.skip_string(')'): + break + else: + self.fail( + 'Expecting "," or ")" in parameters_and_qualifiers, ' + 'got "%s".' % self.current_char) + if paramMode != 'function': - return ASTParametersQualifiers(args, None, None, None, None, None, None, None) - + return ASTParametersQualifiers( + args, None, None, None, None, None, None, None) + self.skip_ws() const = self.skip_word_and_ws('const') volatile = self.skip_word_and_ws('volatile') - if not const: # the can be permuted + if not const: # the can be permuted const = self.skip_word_and_ws('const') - + refQual = None - if self.skip_string('&&'): refQual = '&&' - if not refQual and self.skip_string('&'): refQual = '&' - + if self.skip_string('&&'): + refQual = '&&' + if not refQual and self.skip_string('&'): + refQual = '&' + exceptionSpec = None override = None final = None @@ -1153,30 +1314,42 @@ class DefinitionParser(object): self.skip_ws() if self.skip_string('('): self.fail('Parameterised "noexcept" not implemented.') - + self.skip_ws() override = self.skip_word_and_ws('override') final = self.skip_word_and_ws('final') - if not override: override = self.skip_word_and_ws('override') # they can be permuted - + if not override: + override = self.skip_word_and_ws( + 'override') # they can be permuted + self.skip_ws() if self.skip_string('='): self.skip_ws() - valid = {'0', 'delete', 'default'} + valid = ('0', 'delete', 'default') for w in valid: if self.skip_word_and_ws(w): initializer = w break if not initializer: - self.fail('Expected "%s" in initializer-specifier.' % u'" or "'.join(valid)) - - return ASTParametersQualifiers(args, volatile, const, refQual, exceptionSpec, override, final, initializer) - + self.fail( + 'Expected "%s" in initializer-specifier.' + % u'" or "'.join(valid)) + + return ASTParametersQualifiers( + args, volatile, const, refQual, exceptionSpec, override, final, + initializer) + def _parse_decl_specs(self, outer, typed=True): """ - visibility storage-class-specifier function-specifier "constexpr" "volatile" "const" trailing-type-specifier - storage-class-specifier -> "static" (only for member_object and function_object) - function-specifier -> "inline" | "virtual" | "explicit" (only for function_object) + visibility storage-class-specifier function-specifier "constexpr" + "volatile" "const" trailing-type-specifier + + storage-class-specifier -> "static" (only for member_object and + function_object) + + function-specifier -> "inline" | "virtual" | "explicit" (only for + function_object) + "constexpr" (only for member_object and function_object) """ visibility = None @@ -1187,16 +1360,16 @@ class DefinitionParser(object): constexpr = None volatile = None const = None - + if outer: self.skip_ws() if self.match(_visibility_re): visibility = self.matched_text - - while 1: # accept any permutation of a subset of some decl-specs + + while 1: # accept any permutation of a subset of some decl-specs self.skip_ws() if not storage: - if outer in {'member', 'function'}: + if outer in ('member', 'function'): if self.skip_word('static'): storage = 'static' continue @@ -1204,57 +1377,72 @@ class DefinitionParser(object): if self.skip_word('mutable'): storage = 'mutable' continue - if outer == 'fuction': # TODO: maybe in more contexts, missing test cases + if outer == 'fuction': + # TODO: maybe in more contexts, missing test cases if self.skip_word('register'): storage = 'register' continue - + if outer == 'function': # function-specifiers if not inline: inline = self.skip_word('inline') - if inline: continue + if inline: + continue if not virtual: virtual = self.skip_word('virtual') - if virtual: continue + if virtual: + continue if not explicit: explicit = self.skip_word('explicit') - if explicit: continue - - if not constexpr and outer in {'member', 'function'}: + if explicit: + continue + + if not constexpr and outer in ('member', 'function'): constexpr = self.skip_word("constexpr") - if constexpr: continue + if constexpr: + continue if not volatile and typed: volatile = self.skip_word('volatile') - if volatile: continue + if volatile: + continue if not const and typed: const = self.skip_word('const') - if const: continue + if const: + continue break - - if typed: trailing = self._parse_trailing_type_spec() - else: trailing = None - return ASTDeclSpecs(outer, visibility, storage, inline, virtual, explicit, constexpr, volatile, const, trailing) - + + if typed: + trailing = self._parse_trailing_type_spec() + else: + trailing = None + return ASTDeclSpecs( + outer, visibility, storage, inline, virtual, explicit, constexpr, + volatile, const, trailing) + def _parse_declerator(self, named, paramMode=None, typed=True): if paramMode: - if not paramMode in {'type', 'function'}: - raise Exception("Internal error, unknown paramMode '%s'." % paramMode) + if not paramMode in ('type', 'function'): + raise Exception( + "Internal error, unknown paramMode '%s'." % paramMode) ptrOps = [] while 1: - if not typed: break + if not typed: + break self.skip_ws() if self.skip_string('*'): self.skip_ws() volatile = self.skip_word_and_ws('volatile') const = self.skip_word_and_ws('const') ptrOps.append(ASTPtrOpPtr(volatile=volatile, const=const)) - elif self.skip_string('&'): ptrOps.append(ASTPtrOpRef()) + elif self.skip_string('&'): + ptrOps.append(ASTPtrOpRef()) elif self.skip_string('...'): ptrOps.append(ASTPtrOpParamPack()) break - else: break - + else: + break + if named == 'maybe': try: declId = self._parse_nested_name() @@ -1264,7 +1452,7 @@ class DefinitionParser(object): declId = self._parse_nested_name() else: declId = None - + suffixOpts = [] while 1: self.skip_ws() @@ -1273,32 +1461,39 @@ class DefinitionParser(object): openCount = 1 while not self.eof: c = self.current_char - if c == '[': openCount += 1 - elif c == ']': openCount -= 1 - if openCount == 0: break + if c == '[': + openCount += 1 + elif c == ']': + openCount -= 1 + if openCount == 0: + break self.pos += 1 if self.eof: self.pos = startPos - self.fail("Could not find closing square bracket for array.") + self.fail( + "Could not find closing square bracket for array.") self.pos += 1 - suffixOpts.append(ASTArray(self.definition[startPos+1:self.pos-1].strip())) + suffixOpts.append(ASTArray( + self.definition[startPos + 1:self.pos - 1].strip())) continue if paramMode: paramQual = self._parse_parameters_and_qualifiers(paramMode) - if paramQual: suffixOpts.append(paramQual) + if paramQual: + suffixOpts.append(paramQual) break - + return ASTDeclerator(ptrOps, declId, suffixOpts) - + def _parse_initializer(self, outer=None): self.skip_ws() # TODO: support paren and brace initialization for memberObject - if not self.skip_string('='): return None + if not self.skip_string('='): + return None else: if outer == 'member': value = self.read_rest().strip() return ASTInitializer(value) - elif outer == None: # function parameter + elif outer == None: # function parameter symbols = [] startPos = self.pos self.skip_ws() @@ -1306,36 +1501,47 @@ class DefinitionParser(object): value = self.matched_text return ASTInitializer(value) while not self.eof: - if len(symbols) == 0 and self.current_char in {',', ')'}: break - elif len(symbols) > 0 and self.current_char == symbols[-1]: symbols.pop() - elif self.current_char == '(': symbols.append(')') - # TODO: actually implement nice handling of quotes, braces, brackets, parens, and whatever + if len(symbols) == 0 and self.current_char in (',', ')'): + break + elif len(symbols) > 0 and self.current_char == symbols[-1]: + symbols.pop() + elif self.current_char == '(': + symbols.append(')') + # TODO: actually implement nice handling of quotes, braces, + # brackets, parens, and whatever self.pos += 1 if self.eof: self.pos = startPos - self.fail('Could not find end of default value for function parameter.') + self.fail( + 'Could not find end of default value for function ' + 'parameter.') value = self.definition[startPos:self.pos].strip() return ASTInitializer(value) else: - self.fail("Internal error, initializer for outer '%s' not implemented." % outer) - + self.fail( + "Internal error, initializer for outer '%s' not " + "implemented." % outer) + def _parse_type(self, outer=None, named=False, allowParams=False): """ - named=False|'maybe'|True: 'maybe' is e.g., for function objects which doesn't need to name the arguments + named=False|'maybe'|True: 'maybe' is e.g., for function objects which + doesn't need to name the arguments """ - if outer: # always named - if not outer in {'type', 'member', 'function'}: + if outer: # always named + if not outer in ('type', 'member', 'function'): raise Exception('Internal error, unknown outer "%s".' % outer) assert not named - - if outer in {'type', 'function'}: + + if outer in ('type', 'function'): # We allow type objects to just be a name. - # Some functions don't have normal return types: constructors, destrutors, cast operators + # Some functions don't have normal return types: constructors, + # destrutors, cast operators startPos = self.pos # first try without the type try: declSpecs = self._parse_decl_specs(outer=outer, typed=False) - decl = self._parse_declerator(named=True, paramMode=outer, typed=False) + decl = self._parse_declerator(named=True, paramMode=outer, + typed=False) self.assert_end() except DefinitionError as exUntyped: self.pos = startPos @@ -1344,28 +1550,38 @@ class DefinitionParser(object): decl = self._parse_declerator(named=True, paramMode=outer) except DefinitionError as exTyped: if outer == 'type': - raise DefinitionError('Type must be either just a name or a typedef-like declaration.\nJust a name error: %s\nTypedef-like expression error: %s' - % (exUntyped.description, exTyped.description)) - else: # do it again to get the proper traceback (how do you relieable save a traceback when an exception is constructed?) + raise DefinitionError( + 'Type must be either just a name or a ' + 'typedef-like declaration.\nJust a name error: ' + '%s\nTypedef-like expression error: %s' + % (exUntyped.description, exTyped.description)) + else: + # do it again to get the proper traceback (how do you + # relieable save a traceback when an exception is + # constructed?) self.pos = startPos declSpecs = self._parse_decl_specs(outer=outer) - decl = self._parse_declerator(named=True, paramMode=outer) + decl = self._parse_declerator(named=True, + paramMode=outer) else: if outer: named = True allowParams = True - if allowParams: paramMode = 'type' - else: paramMode = None + if allowParams: + paramMode = 'type' + else: + paramMode = None declSpecs = self._parse_decl_specs(outer=outer) decl = self._parse_declerator(named=named, paramMode=paramMode) return ASTType(declSpecs, decl) - + def _parse_type_with_init(self, outer=None, named=False): - if outer: assert outer in {'type', 'member', 'function'} + if outer: + assert outer in ('type', 'member', 'function') type = self._parse_type(outer=outer, named=named) init = self._parse_initializer(outer=outer) return ASTTypeWithInit(type, init) - + def _parse_class(self): name = self._parse_nested_name() bases = [] @@ -1379,40 +1595,43 @@ class DefinitionParser(object): baseName = self._parse_nested_name() bases.append(ASTBaseClass(baseName, visibility)) self.skip_ws() - if self.skip_string(','): continue - else: break + if self.skip_string(','): + continue + else: + break return ASTClass(name, bases) - + def parse_type_object(self): res = self._parse_type(outer='type') res.objectType = 'type' return res - + def parse_member_object(self): res = self._parse_type_with_init(outer='member') res.objectType = 'member' return res - + def parse_function_object(self): res = self._parse_type(outer='function') res.objectType = 'function' return res - + def parse_class_object(self): res = self._parse_class() res.objectType = 'class' return res - + def parse_namespace_object(self): res = self._parse_nested_name() res.objectType = 'namespace' return res - + def parse_xref_object(self): res = self._parse_nested_name() res.objectType = 'xref' return res + class CPPObject(ObjectDescription): """Description of a C++ language object.""" @@ -1439,18 +1658,22 @@ class CPPObject(ObjectDescription): signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) if not name in objects: - objects.setdefault(name, (self.env.docname, ast.objectType, theid)) + objects.setdefault(name, + (self.env.docname, ast.objectType, theid)) # add the uninstantiated template if it doesn't exist uninstantiated = ast.prefixedName.get_name_no_last_template() if uninstantiated != name and uninstantiated not in objects: signode['names'].append(uninstantiated) - objects.setdefault(uninstantiated, (self.env.docname, ast.objectType, theid)) + objects.setdefault(uninstantiated, ( + self.env.docname, ast.objectType, theid)) self.env.temp_data['cpp:lastname'] = ast.prefixedName indextext = self.get_index_text(name) if not re.compile(r'^[a-zA-Z0-9_]*$').match(theid): - self.state_machine.reporter.warning('Index id generation for C++ object "%s" failed, please report as bug (id=%s).' % (text_type(ast), theid), - line=self.lineno) + self.state_machine.reporter.warning( + 'Index id generation for C++ object "%s" failed, please ' + 'report as bug (id=%s).' % (text_type(ast), theid), + line=self.lineno) self.indexnode['entries'].append(('single', indextext, theid, '')) def parse_definition(self, parser): @@ -1465,7 +1688,8 @@ class CPPObject(ObjectDescription): ast = self.parse_definition(parser) parser.assert_end() except DefinitionError as e: - self.state_machine.reporter.warning(e.description, line=self.lineno) + self.state_machine.reporter.warning(e.description, + line=self.lineno) raise ValueError self.describe_signature(signode, ast) @@ -1477,31 +1701,31 @@ class CPPObject(ObjectDescription): ast.prefixedName = ast.name return ast + class CPPTypeObject(CPPObject): - def get_index_text(self, name): return _('%s (C++ type)') % name - + def parse_definition(self, parser): return parser.parse_type_object() - + def describe_signature(self, signode, ast): signode += addnodes.desc_annotation('type ', 'type ') ast.describe_signature(signode, 'lastIsName', self.env) - -class CPPMemberObject(CPPObject): + +class CPPMemberObject(CPPObject): def get_index_text(self, name): return _('%s (C++ member)') % name def parse_definition(self, parser): return parser.parse_member_object() - + def describe_signature(self, signode, ast): ast.describe_signature(signode, 'lastIsName', self.env) - + + class CPPFunctionObject(CPPObject): - def get_index_text(self, name): return _('%s (C++ function)') % name @@ -1511,8 +1735,8 @@ class CPPFunctionObject(CPPObject): def describe_signature(self, signode, ast): ast.describe_signature(signode, 'lastIsName', self.env) -class CPPClassObject(CPPObject): +class CPPClassObject(CPPObject): def get_index_text(self, name): return _('%s (C++ class)') % name @@ -1533,7 +1757,8 @@ class CPPClassObject(CPPObject): def describe_signature(self, signode, ast): signode += addnodes.desc_annotation('class ', 'class ') ast.describe_signature(signode, 'lastIsName', self.env) - + + class CPPNamespaceObject(Directive): """ This directive is just to tell Sphinx that we're documenting stuff in @@ -1556,13 +1781,14 @@ class CPPNamespaceObject(Directive): prefix = parser.parse_namespace_object() parser.assert_end() except DefinitionError as e: - self.state_machine.reporter.warning(e.description,line=self.lineno) + self.state_machine.reporter.warning(e.description, + line=self.lineno) else: env.temp_data['cpp:parent'] = [prefix] return [] -class CPPXRefRole(XRefRole): +class CPPXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): parent = env.temp_data.get('cpp:parent') if parent: @@ -1578,29 +1804,30 @@ class CPPXRefRole(XRefRole): title = title[dcolon + 2:] return title, target + class CPPDomain(Domain): """C++ language domain.""" name = 'cpp' label = 'C++' object_types = { - 'class': ObjType(l_('class'), 'class'), + 'class': ObjType(l_('class'), 'class'), 'function': ObjType(l_('function'), 'func'), - 'member': ObjType(l_('member'), 'member'), - 'type': ObjType(l_('type'), 'type') + 'member': ObjType(l_('member'), 'member'), + 'type': ObjType(l_('type'), 'type') } directives = { - 'class': CPPClassObject, - 'function': CPPFunctionObject, - 'member': CPPMemberObject, - 'type': CPPTypeObject, - 'namespace': CPPNamespaceObject + 'class': CPPClassObject, + 'function': CPPFunctionObject, + 'member': CPPMemberObject, + 'type': CPPTypeObject, + 'namespace': CPPNamespaceObject } roles = { - 'class': CPPXRefRole(), - 'func' : CPPXRefRole(fix_parens=True), + 'class': CPPXRefRole(), + 'func': CPPXRefRole(fix_parens=True), 'member': CPPXRefRole(), - 'type': CPPXRefRole() + 'type': CPPXRefRole() } initial_data = { 'objects': {}, # prefixedName -> (docname, objectType, id) @@ -1621,26 +1848,30 @@ class CPPDomain(Domain): if name not in self.data['objects']: return None docname, objectType, id = self.data['objects'][name] - return make_refnode(builder, fromdocname, docname, id, contnode, name) + return make_refnode(builder, fromdocname, docname, id, contnode, + name) parser = DefinitionParser(target) try: nameAst = parser.parse_xref_object().name parser.skip_ws() - if not parser.eof: raise DefinitionError('') + if not parser.eof: + raise DefinitionError('') except DefinitionError: env.warn_node('unparseable C++ definition: %r' % target, node) return None # try as is the name is fully qualified refNode = _create_refnode(nameAst) - if refNode: return refNode - + if refNode: + return refNode + # try qualifying it with the parent parent = node.get('cpp:parent', None) if parent and len(parent) > 0: return _create_refnode(nameAst.prefix_nested_name(parent[-1])) - else: return None + else: + return None def get_objects(self): for refname, (docname, type, theid) in iteritems(self.data['objects']):