# -*- coding: utf-8 -*- """ sphinx.domains.cpp ~~~~~~~~~~~~~~~~~~ The C++ language domain. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import re from copy import deepcopy from docutils import nodes from sphinx import addnodes from sphinx.roles import XRefRole from sphinx.locale import l_, _ from sphinx.domains import Domain, ObjType from sphinx.directives import ObjectDescription from sphinx.util.nodes import make_refnode from sphinx.util.compat import Directive from sphinx.util.docfields import Field, GroupedField _identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b') _whitespace_re = re.compile(r'\s+(?u)') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) _visibility_re = re.compile(r'\b(public|private|protected)\b') _array_def_re = re.compile(r'\[\s*([^\]]+?)?\s*\]') _template_arg_re = re.compile(r'(%s)|([^,>]+)' % _string_re.pattern, re.S) _operator_re = re.compile(r'''(?x) \[\s*\] | \(\s*\) | \+\+ | -- | ->\*? | \, | (<<|>>)=? | && | \|\| | [!<>=/*%+|&^~-]=? ''') _id_shortwords = { 'char': 'c', 'signed char': 'c', 'unsigned char': 'C', 'int': 'i', 'signed int': 'i', 'unsigned int': 'U', 'long': 'l', 'signed long': 'l', 'unsigned long': 'L', 'bool': 'b', 'size_t': 's', 'std::string': 'ss', 'std::ostream': 'os', 'std::istream': 'is', 'std::iostream': 'ios', 'std::vector': 'v', 'std::map': 'm', 'operator[]': 'subscript-operator', 'operator()': 'call-operator', 'operator!': 'not-operator', 'operator<': 'lt-operator', 'operator<=': 'lte-operator', 'operator>': 'gt-operator', 'operator>=': 'gte-operator', 'operator=': 'assign-operator', 'operator/': 'div-operator', 'operator*': 'mul-operator', 'operator%': 'mod-operator', 'operator+': 'add-operator', 'operator-': 'sub-operator', 'operator|': 'or-operator', 'operator&': 'and-operator', 'operator^': 'xor-operator', 'operator&&': 'sand-operator', 'operator||': 'sor-operator', 'operator==': 'eq-operator', 'operator!=': 'neq-operator', 'operator<<': 'lshift-operator', 'operator>>': 'rshift-operator', 'operator-=': 'sub-assign-operator', 'operator+=': 'add-assign-operator', 'operator*-': 'mul-assign-operator', 'operator/=': 'div-assign-operator', 'operator%=': 'mod-assign-operator', 'operator&=': 'and-assign-operator', 'operator|=': 'or-assign-operator', 'operator<<=': 'lshift-assign-operator', 'operator>>=': 'rshift-assign-operator', 'operator^=': 'xor-assign-operator', 'operator,': 'comma-operator', 'operator->': 'pointer-operator', 'operator->*': 'pointer-by-pointer-operator', 'operator~': 'inv-operator', 'operator++': 'inc-operator', 'operator--': 'dec-operator', 'operator new': 'new-operator', 'operator new[]': 'new-array-operator', 'operator delete': 'delete-operator', 'operator delete[]': 'delete-array-operator' } class DefinitionError(Exception): def __init__(self, description): self.description = description def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.description class DefExpr(object): def __eq__(self, other): if type(self) is not type(other): return False try: for key, value in self.__dict__.iteritems(): if value != getattr(other, key): return False except AttributeError: return False return True def __ne__(self, other): return not self.__eq__(other) __hash__ = None def clone(self): """Clone a definition expression node.""" return deepcopy(self) def get_id(self): """Return the id for the node.""" return u'' def get_name(self): """Return the name. Returns either `None` or a node with a name you might call :meth:`split_owner` on. """ return None def split_owner(self): """Nodes returned by :meth:`get_name` can split off their owning parent. This function returns the owner and the name as a tuple of two items. If a node does not support it, it returns None as owner and self as name. """ return None, self def prefix(self, prefix): """Prefix a name node (a node returned by :meth:`get_name`).""" raise NotImplementedError() def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): raise NotImplementedError() def __repr__(self): return '<%s %s>' % (self.__class__.__name__, self) class PrimaryDefExpr(DefExpr): def get_name(self): return self def prefix(self, prefix): if isinstance(prefix, PathDefExpr): prefix = prefix.clone() prefix.path.append(self) return prefix return PathDefExpr([prefix, self]) class NameDefExpr(PrimaryDefExpr): def __init__(self, name): self.name = name def get_id(self): name = _id_shortwords.get(self.name) if name is not None: return name return self.name.replace(u' ', u'-') def __unicode__(self): return unicode(self.name) class PathDefExpr(PrimaryDefExpr): def __init__(self, parts): self.path = parts def get_id(self): rv = u'::'.join(x.get_id() for x in self.path) return _id_shortwords.get(rv, rv) def split_owner(self): if len(self.path) > 1: return PathDefExpr(self.path[:-1]), self.path[-1] return None, self def prefix(self, prefix): if isinstance(prefix, PathDefExpr): prefix = prefix.clone() prefix.path.extend(self.path) return prefix return PathDefExpr([prefix] + self.path) def __unicode__(self): return u'::'.join(map(unicode, self.path)) class ArrayTypeSuffixDefExpr(object): def __init__(self, size_hint=None): self.size_hint = size_hint def get_id_suffix(self): return 'A' def __unicode__(self): return u'[%s]' % ( self.size_hint is not None and unicode(self.size_hint) or u'', ) class TemplateDefExpr(PrimaryDefExpr): def __init__(self, typename, args): self.typename = typename self.args = args def split_owner(self): owner, typename = self.typename.split_owner() return owner, TemplateDefExpr(typename, self.args) def get_id(self): return u'%s:%s:' % (self.typename.get_id(), u'.'.join(x.get_id() for x in self.args)) def __unicode__(self): return u'%s<%s>' % (self.typename, u', '.join(map(unicode, self.args))) class ConstantTemplateArgExpr(PrimaryDefExpr): def __init__(self, arg): self.arg = arg def get_id(self): return self.arg.replace(u' ', u'-') def __unicode__(self): return unicode(self.arg) class WrappingDefExpr(DefExpr): def __init__(self, typename): self.typename = typename def get_name(self): return self.typename.get_name() class ModifierDefExpr(WrappingDefExpr): def __init__(self, typename, modifiers): WrappingDefExpr.__init__(self, typename) self.modifiers = modifiers def get_id(self): pieces = [_id_shortwords.get(unicode(x), unicode(x)) for x in self.modifiers] pieces.append(self.typename.get_id()) return u'-'.join(pieces) def __unicode__(self): return u' '.join(map(unicode, list(self.modifiers) + [self.typename])) class PtrDefExpr(WrappingDefExpr): def get_id(self): return self.typename.get_id() + u'P' def __unicode__(self): return u'%s*' % self.typename class LValRefDefExpr(WrappingDefExpr): def get_id(self): return self.typename.get_id() + u'R' def __unicode__(self): return u'%s&' % self.typename class RValRefDefExpr(WrappingDefExpr): def get_id(self): return self.typename.get_id() + u'RR' def __unicode__(self): return u'%s&&' % self.typename class ConstDefExpr(WrappingDefExpr): def __init__(self, typename, prefix=False): WrappingDefExpr.__init__(self, typename) self.prefix = prefix def get_id(self): return self.typename.get_id() + u'C' def __unicode__(self): return (self.prefix and u'const %s' or u'%s const') % self.typename class CastOpDefExpr(PrimaryDefExpr): def __init__(self, typename): self.typename = typename def get_id(self): return u'castto-%s-operator' % self.typename.get_id() def __unicode__(self): return u'operator %s' % self.typename class ArgumentDefExpr(DefExpr): def __init__(self, type, name, type_suffixes, default=None): self.name = name self.type = type self.type_suffixes = type_suffixes self.default = default def get_name(self): return self.name.get_name() def get_id(self): buf = [] buf.append(self.type and self.type.get_id() or 'X') for suffix in self.type_suffixes: buf.append(suffix.get_id_suffix()) return u''.join(buf) def __unicode__(self): buf = [(u'%s %s' % (self.type or u'', self.name or u'')).strip()] if self.default is not None: buf.append('=%s' % self.default) for suffix in self.type_suffixes: buf.append(unicode(suffix)) return u''.join(buf) class NamedDefExpr(DefExpr): def __init__(self, name, visibility, static): self.name = name self.visibility = visibility self.static = static def get_name(self): return self.name.get_name() def get_modifiers(self, visibility='public'): rv = [] if self.visibility != visibility: rv.append(self.visibility) if self.static: rv.append(u'static') return rv class TypeObjDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, typename, type_suffixes): NamedDefExpr.__init__(self, name, visibility, static) self.typename = typename self.type_suffixes = type_suffixes def get_id(self): if self.typename is None: buf = [self.name.get_id()] else: buf = [u'%s__%s' % (self.name.get_id(), self.typename.get_id())] for suffix in self.type_suffixes: buf.append(suffix.get_id_suffix()) return u''.join(buf) def __unicode__(self): buf = self.get_modifiers() if self.typename is None: buf.append(unicode(self.name)) else: buf.extend(map(unicode, (self.typename, self.name))) buf = [u' '.join(buf)] for suffix in self.type_suffixes: buf.append(unicode(suffix)) return u''.join(buf) class MemberObjDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, typename, type_suffixes, value): NamedDefExpr.__init__(self, name, visibility, static) self.typename = typename self.type_suffixes = type_suffixes self.value = value def get_id(self): buf = [u'%s__%s' % (self.name.get_id(), self.typename.get_id())] for suffix in self.type_suffixes: buf.append(suffix.get_id_suffix()) return u''.join(buf) def __unicode__(self): buf = self.get_modifiers() buf.extend((unicode(self.typename), unicode(self.name))) buf = [u' '.join(buf)] for suffix in self.type_suffixes: buf.append(unicode(suffix)) if self.value is not None: buf.append(u' = %s' % self.value) return u''.join(buf) class FuncDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, explicit, constexpr, rv, signature, const, noexcept, pure_virtual): NamedDefExpr.__init__(self, name, visibility, static) self.rv = rv self.signature = signature self.explicit = explicit self.constexpr = constexpr self.const = const self.noexcept = noexcept self.pure_virtual = pure_virtual def get_id(self): return u'%s%s%s%s' % ( self.name.get_id(), self.signature and u'__' + u'.'.join(x.get_id() for x in self.signature) or u'', self.const and u'C' or u'', self.constexpr and 'CE' or '' ) def __unicode__(self): buf = self.get_modifiers() if self.explicit: buf.append(u'explicit') if self.constexpr: buf.append(u'constexpr') if self.rv is not None: buf.append(unicode(self.rv)) buf.append(u'%s(%s)' % (self.name, u', '.join( map(unicode, self.signature)))) if self.const: buf.append(u'const') if self.noexcept: buf.append(u'noexcept') if self.pure_virtual: buf.append(u'= 0') return u' '.join(buf) class ClassDefExpr(NamedDefExpr): def __init__(self, name, visibility, static, bases): NamedDefExpr.__init__(self, name, visibility, static) self.bases = bases def get_id(self): return self.name.get_id() def _tostring(self, visibility='public'): buf = self.get_modifiers(visibility) buf.append(unicode(self.name)) if self.bases: buf.append(u':') buf.append(u', '.join(base._tostring('private') for base in self.bases)) return u' '.join(buf) def __unicode__(self): return self._tostring('public') class DefinitionParser(object): # mapping of valid type modifiers. if the set is None it means # the modifier can prefix all types, otherwise only the types # (actually more keywords) in the set. Also check # _guess_typename when changing this. _modifiers = { 'volatile': None, 'register': None, 'mutable': None, 'const': None, 'typename': None, 'unsigned': set(('char', 'short', 'int', 'long')), 'signed': set(('char', 'short', 'int', 'long')), 'short': set(('int',)), 'long': set(('int', 'long', 'double')) } def __init__(self, definition): self.definition = definition.strip() self.pos = 0 self.end = len(self.definition) self.last_match = None self._previous_state = (0, None) def fail(self, msg): raise DefinitionError('Invalid definition: %s [error at %d]\n %s' % (msg, self.pos, self.definition)) def match(self, regex): match = regex.match(self.definition, self.pos) if match is not None: self._previous_state = (self.pos, self.last_match) self.pos = match.end() self.last_match = match return True return False def backout(self): self.pos, self.last_match = self._previous_state def skip_string(self, string): strlen = len(string) if self.definition[self.pos:self.pos + strlen] == string: self.pos += strlen return True return False def skip_word(self, word): return self.match(re.compile(r'\b%s\b' % re.escape(word))) def skip_ws(self): return self.match(_whitespace_re) def skip_word_and_ws(self, word): if self.skip_word(word): self.skip_ws() return True return False @property def eof(self): return self.pos >= self.end @property def current_char(self): try: return self.definition[self.pos] except IndexError: return 'EOF' @property def matched_text(self): if self.last_match is not None: return self.last_match.group() def _parse_operator(self): self.skip_ws() # thank god, a regular operator definition if self.match(_operator_re): return NameDefExpr('operator' + _whitespace_re.sub('', self.matched_text)) # new/delete operator? for allocop in 'new', 'delete': if not self.skip_word(allocop): continue self.skip_ws() if self.skip_string('['): self.skip_ws() if not self.skip_string(']'): self.fail('expected "]" for ' + allocop) allocop += '[]' return NameDefExpr('operator ' + allocop) # oh well, looks like a cast operator definition. # In that case, eat another type. type = self._parse_type() return CastOpDefExpr(type) def _parse_name(self): return self._parse_name_or_template_arg(False) def _parse_name_or_template_arg(self, in_template): if not self.match(_identifier_re): if not in_template: self.fail('expected name') if not self.match(_template_arg_re): self.fail('expected name or constant template argument') return ConstantTemplateArgExpr(self.matched_text.strip()) identifier = self.matched_text # strictly speaking, operators are not regular identifiers # but because operator is a keyword, it might not be used # for variable names anyways, so we can safely parse the # operator here as identifier if identifier == 'operator': return self._parse_operator() return NameDefExpr(identifier) def _guess_typename(self, path): if not path: return [], 'int' # for the long type, we don't want the int in there if 'long' in path: path = [x for x in path if x != 'int'] # remove one long path.remove('long') return path, 'long' if path[-1] in ('int', 'char'): return path[:-1], path[-1] return path, 'int' def _attach_crefptr(self, expr, is_const=False): if is_const: expr = ConstDefExpr(expr, prefix=True) while 1: self.skip_ws() if self.skip_word('const'): expr = ConstDefExpr(expr) elif self.skip_string('*'): expr = PtrDefExpr(expr) elif self.skip_string('&'): if self.skip_string('&'): expr = RValRefDefExpr(expr) else: expr = LValRefDefExpr(expr) else: return expr def _try_parse_type_suffixes(self): rv = [] while self.match(_array_def_re): rv.append(ArrayTypeSuffixDefExpr(self.last_match.group(1))) self.skip_ws() return rv def _peek_const(self, path): try: path.remove('const') return True except ValueError: return False def _parse_builtin(self, modifier): path = [modifier] following = self._modifiers[modifier] while 1: self.skip_ws() if not self.match(_identifier_re): break identifier = self.matched_text if identifier in following: path.append(identifier) following = self._modifiers[modifier] assert following else: self.backout() break is_const = self._peek_const(path) modifiers, typename = self._guess_typename(path) rv = ModifierDefExpr(NameDefExpr(typename), modifiers) return self._attach_crefptr(rv, is_const) def _parse_type_expr(self, in_template=False): typename = self._parse_name_or_template_arg(in_template) self.skip_ws() if not self.skip_string('<'): return typename args = [] while 1: self.skip_ws() if self.skip_string('>'): break if args: if not self.skip_string(','): self.fail('"," or ">" in template expected') self.skip_ws() args.append(self._parse_type(True)) return TemplateDefExpr(typename, args) def _parse_type(self, in_template=False): self.skip_ws() result = [] modifiers = [] # if there is a leading :: or not, we don't care because we # treat them exactly the same. Buf *if* there is one, we # don't have to check for type modifiers if not self.skip_string('::'): self.skip_ws() while self.match(_identifier_re): modifier = self.matched_text if modifier in self._modifiers: following = self._modifiers[modifier] # if the set is not none, there is a limited set # of types that might follow. It is technically # impossible for a template to follow, so what # we do is go to a different function that just # eats types if following is not None: return self._parse_builtin(modifier) modifiers.append(modifier) else: self.backout() break while 1: self.skip_ws() if (in_template and self.current_char in ',>') or \ (result and not self.skip_string('::')) or \ self.eof: break result.append(self._parse_type_expr(in_template)) if not result: self.fail('expected type') if len(result) == 1: rv = result[0] else: rv = PathDefExpr(result) is_const = self._peek_const(modifiers) if modifiers: rv = ModifierDefExpr(rv, modifiers) return self._attach_crefptr(rv, is_const) def _parse_default_expr(self): self.skip_ws() if self.match(_string_re): return self.matched_text idx1 = self.definition.find(',', self.pos) idx2 = self.definition.find(')', self.pos) if idx1 < 0: idx = idx2 elif idx2 < 0: idx = idx1 else: idx = min(idx1, idx2) if idx < 0: self.fail('unexpected end in default expression') rv = self.definition[self.pos:idx] self.pos = idx return rv def _parse_signature(self): self.skip_ws() if not self.skip_string('('): self.fail('expected parentheses for function') args = [] while 1: self.skip_ws() if self.eof: self.fail('missing closing parentheses') if self.skip_string(')'): break if args: if not self.skip_string(','): self.fail('expected comma between arguments') self.skip_ws() if self.skip_string('...'): args.append(ArgumentDefExpr(None, '...', [], None)) if self.skip_string(')'): break else: self.fail('expected closing parenthesis after ellipses') argname = default = None argtype = self._parse_type() self.skip_ws() type_suffixes = self._try_parse_type_suffixes() if self.skip_string('='): default = self._parse_default_expr() elif self.current_char not in ',)': argname = self._parse_name() self.skip_ws() type_suffixes.extend(self._try_parse_type_suffixes()) if self.skip_string('='): default = self._parse_default_expr() if argname is None: argname = argtype argtype = None args.append(ArgumentDefExpr(argtype, argname, type_suffixes, default)) self.skip_ws() const = self.skip_word_and_ws('const') noexcept = self.skip_word_and_ws('noexcept') if self.skip_string('='): self.skip_ws() if not (self.skip_string('0') or \ self.skip_word('NULL') or \ self.skip_word('nullptr')): self.fail('pure virtual functions must be defined with ' 'either 0, NULL or nullptr, other macros are ' 'not allowed') pure_virtual = True else: pure_virtual = False return args, const, noexcept, pure_virtual def _parse_visibility_static(self): visibility = 'public' if self.match(_visibility_re): visibility = self.matched_text static = self.skip_word('static') return visibility, static def parse_type(self): return self._parse_type() def parse_type_object(self): visibility, static = self._parse_visibility_static() typename = self._parse_type() self.skip_ws() if not self.eof: name = self._parse_type() type_suffixes = self._try_parse_type_suffixes() else: name = typename typename = None type_suffixes = [] return TypeObjDefExpr(name, visibility, static, typename, type_suffixes) def parse_member_object(self): visibility, static = self._parse_visibility_static() typename = self._parse_type() name = self._parse_type() type_suffixes = self._try_parse_type_suffixes() self.skip_ws() if self.skip_string('='): value = self.read_rest().strip() else: value = None return MemberObjDefExpr(name, visibility, static, typename, type_suffixes, value) def parse_function(self): visibility, static = self._parse_visibility_static() explicit = self.skip_word_and_ws('explicit') constexpr = self.skip_word_and_ws('constexpr') rv = self._parse_type() self.skip_ws() # some things just don't have return values if self.current_char == '(': name = rv rv = None else: name = self._parse_type() return FuncDefExpr(name, visibility, static, explicit, constexpr, rv, *self._parse_signature()) def parse_class(self): visibility, static = self._parse_visibility_static() name = self._parse_type() bases = [] if self.skip_string(':'): self.skip_ws() while 1: access = 'private' if self.match(_visibility_re): access = self.matched_text base = self._parse_type() bases.append(ClassDefExpr(base, access, False, [])) if self.skip_string(','): self.skip_ws() else: break return ClassDefExpr(name, visibility, static, bases) def read_rest(self): rv = self.definition[self.pos:] self.pos = self.end return rv def assert_end(self): self.skip_ws() if not self.eof: self.fail('expected end of definition, got %r' % self.definition[self.pos:]) class CPPObject(ObjectDescription): """Description of a C++ language object.""" doc_field_types = [ GroupedField('parameter', label=l_('Parameters'), names=('param', 'parameter', 'arg', 'argument'), can_collapse=True), GroupedField('exceptions', label=l_('Throws'), rolename='cpp:class', names=('throws', 'throw', 'exception'), can_collapse=True), Field('returnvalue', label=l_('Returns'), has_arg=False, names=('returns', 'return')), ] def attach_name(self, node, name): owner, name = name.split_owner() varname = unicode(name) if owner is not None: owner = unicode(owner) + '::' node += addnodes.desc_addname(owner, owner) node += addnodes.desc_name(varname, varname) def attach_type_suffixes(self, node, suffixes): for suffix in suffixes: node += nodes.Text(unicode(suffix)) def attach_type(self, node, type): # XXX: link to c? text = unicode(type) pnode = addnodes.pending_xref( '', refdomain='cpp', reftype='type', reftarget=text, modname=None, classname=None) pnode['cpp:parent'] = self.env.temp_data.get('cpp:parent') pnode += nodes.Text(text) node += pnode def attach_modifiers(self, node, obj, visibility='public'): if obj.visibility != visibility: node += addnodes.desc_annotation(obj.visibility, obj.visibility) node += nodes.Text(' ') if obj.static: node += addnodes.desc_annotation('static', 'static') node += nodes.Text(' ') if getattr(obj, 'constexpr', False): node += addnodes.desc_annotation('constexpr', 'constexpr') node += nodes.Text(' ') def add_target_and_index(self, sigobj, sig, signode): theid = sigobj.get_id() name = unicode(sigobj.name) if theid not in self.state.document.ids: signode['names'].append(theid) signode['ids'].append(theid) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) self.env.domaindata['cpp']['objects'].setdefault(name, (self.env.docname, self.objtype, theid)) indextext = self.get_index_text(name) if indextext: self.indexnode['entries'].append(('single', indextext, theid, '')) def before_content(self): lastname = self.names and self.names[-1] if lastname and not self.env.temp_data.get('cpp:parent'): assert isinstance(lastname, NamedDefExpr) self.env.temp_data['cpp:parent'] = lastname.name self.parentname_set = True else: self.parentname_set = False def after_content(self): if self.parentname_set: self.env.temp_data['cpp:parent'] = None def parse_definition(self, parser): raise NotImplementedError() def describe_signature(self, signode, arg): raise NotImplementedError() def handle_signature(self, sig, signode): parser = DefinitionParser(sig) try: rv = self.parse_definition(parser) parser.assert_end() except DefinitionError, e: self.state_machine.reporter.warning(e.description, line=self.lineno) raise ValueError self.describe_signature(signode, rv) parent = self.env.temp_data.get('cpp:parent') if parent is not None: rv = rv.clone() rv.name = rv.name.prefix(parent) return rv class CPPClassObject(CPPObject): def get_index_text(self, name): return _('%s (C++ class)') % name def parse_definition(self, parser): return parser.parse_class() def describe_signature(self, signode, cls): self.attach_modifiers(signode, cls) signode += addnodes.desc_annotation('class ', 'class ') self.attach_name(signode, cls.name) if cls.bases: signode += nodes.Text(' : ') for base in cls.bases: self.attach_modifiers(signode, base, 'private') signode += nodes.emphasis(unicode(base.name), unicode(base.name)) signode += nodes.Text(', ') signode.pop() # remove the trailing comma class CPPTypeObject(CPPObject): def get_index_text(self, name): if self.objtype == 'type': return _('%s (C++ type)') % name return '' def parse_definition(self, parser): return parser.parse_type_object() def describe_signature(self, signode, obj): self.attach_modifiers(signode, obj) signode += addnodes.desc_annotation('type ', 'type ') if obj.typename is not None: self.attach_type(signode, obj.typename) signode += nodes.Text(' ') self.attach_name(signode, obj.name) self.attach_type_suffixes(signode, obj.type_suffixes) class CPPMemberObject(CPPObject): def get_index_text(self, name): if self.objtype == 'member': return _('%s (C++ member)') % name return '' def parse_definition(self, parser): return parser.parse_member_object() def describe_signature(self, signode, obj): self.attach_modifiers(signode, obj) self.attach_type(signode, obj.typename) signode += nodes.Text(' ') self.attach_name(signode, obj.name) self.attach_type_suffixes(signode, obj.type_suffixes) if obj.value is not None: signode += nodes.Text(u' = ' + obj.value) class CPPFunctionObject(CPPObject): def attach_function(self, node, func): owner, name = func.name.split_owner() if owner is not None: owner = unicode(owner) + '::' node += addnodes.desc_addname(owner, owner) # cast operator is special. in this case the return value # is reversed. if isinstance(name, CastOpDefExpr): node += addnodes.desc_name('operator', 'operator') node += nodes.Text(u' ') self.attach_type(node, name.typename) else: funcname = unicode(name) node += addnodes.desc_name(funcname, funcname) paramlist = addnodes.desc_parameterlist() for arg in func.signature: param = addnodes.desc_parameter('', '', noemph=True) if arg.type is not None: self.attach_type(param, arg.type) param += nodes.Text(u' ') param += nodes.emphasis(unicode(arg.name), unicode(arg.name)) self.attach_type_suffixes(param, arg.type_suffixes) if arg.default is not None: def_ = u'=' + unicode(arg.default) param += nodes.emphasis(def_, def_) paramlist += param node += paramlist if func.const: node += addnodes.desc_addname(' const', ' const') if func.noexcept: node += addnodes.desc_addname(' noexcept', ' noexcept') if func.pure_virtual: node += addnodes.desc_addname(' = 0', ' = 0') def get_index_text(self, name): return _('%s (C++ function)') % name def parse_definition(self, parser): return parser.parse_function() def describe_signature(self, signode, func): self.attach_modifiers(signode, func) if func.explicit: signode += addnodes.desc_annotation('explicit', 'explicit') signode += nodes.Text(' ') # return value is None for things with a reverse return value # such as casting operator definitions or constructors # and destructors. if func.rv is not None: self.attach_type(signode, func.rv) signode += nodes.Text(u' ') self.attach_function(signode, func) class CPPCurrentNamespace(Directive): """ This directive is just to tell Sphinx that we're documenting stuff in namespace foo. """ has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = {} def run(self): env = self.state.document.settings.env if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): env.temp_data['cpp:prefix'] = None else: parser = DefinitionParser(self.arguments[0]) try: prefix = parser.parse_type() parser.assert_end() except DefinitionError, e: self.state_machine.reporter.warning(e.description, line=self.lineno) else: env.temp_data['cpp:prefix'] = prefix return [] class CPPXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): refnode['cpp:parent'] = env.temp_data.get('cpp:parent') if not has_explicit_title: target = target.lstrip('~') # only has a meaning for the title # if the first character is a tilde, don't display the module/class # parts of the contents if title[:1] == '~': title = title[1:] dcolon = title.rfind('::') if dcolon != -1: title = title[dcolon + 2:] return title, target class CPPDomain(Domain): """C++ language domain.""" name = 'cpp' label = 'C++' object_types = { 'class': ObjType(l_('class'), 'class'), 'function': ObjType(l_('function'), 'func'), 'member': ObjType(l_('member'), 'member'), 'type': ObjType(l_('type'), 'type') } directives = { 'class': CPPClassObject, 'function': CPPFunctionObject, 'member': CPPMemberObject, 'type': CPPTypeObject, 'namespace': CPPCurrentNamespace } roles = { 'class': CPPXRefRole(), 'func' : CPPXRefRole(fix_parens=True), 'member': CPPXRefRole(), 'type': CPPXRefRole() } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): for fullname, (fn, _, _) in self.data['objects'].items(): if fn == docname: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _create_refnode(expr): name = unicode(expr) if name not in self.data['objects']: return None obj = self.data['objects'][name] if obj[1] not in self.objtypes_for_role(typ): return None return make_refnode(builder, fromdocname, obj[0], obj[2], contnode, name) parser = DefinitionParser(target) try: expr = parser.parse_type().get_name() parser.skip_ws() if not parser.eof or expr is None: raise DefinitionError('') except DefinitionError: env.warn_node('unparseable C++ definition: %r' % target, node) return None parent = node.get('cpp:parent', None) rv = _create_refnode(expr) if rv is not None or parent is None: return rv parent = parent.get_name() rv = _create_refnode(expr.prefix(parent)) if rv is not None: return rv parent, name = parent.split_owner() return _create_refnode(expr.prefix(parent)) def get_objects(self): for refname, (docname, type, theid) in self.data['objects'].iteritems(): yield (refname, refname, type, docname, refname, 1)