summaryrefslogtreecommitdiff
path: root/sphinx/domains/cpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r--sphinx/domains/cpp.py2983
1 files changed, 2297 insertions, 686 deletions
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 8e74abc96..fcd352e71 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -9,6 +9,22 @@
:license: BSD, see LICENSE for details.
"""
+import re
+from copy import deepcopy
+
+from six import iteritems, text_type
+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.pycompat import UnicodeMixin
+from sphinx.util.docfields import Field, GroupedField
+
"""
Important note on ids:
Multiple id generation schemes are used due to backwards compatibility.
@@ -22,20 +38,45 @@
All versions are generated and attached to elements. The newest is used for
the index. All of the versions should work as permalinks.
- See http://www.nongnu.org/hcb/ for the grammar.
+ See http://www.nongnu.org/hcb/ for the grammar,
+ or https://github.com/cplusplus/draft/blob/master/source/grammar.tex
+ for the newest grammar.
common grammar things:
- simple-declaration
- -> attribute-specifier-seq[opt] decl-specifier-seq[opt]
- init-declarator-list[opt] ;
+ template-declaration ->
+ "template" "<" template-parameter-list ">" declaration
+ template-parameter-list ->
+ template-parameter
+ | template-parameter-list "," template-parameter
+ template-parameter ->
+ type-parameter
+ | parameter-declaration # i.e., same as a function argument
+
+ type-parameter ->
+ "class" "..."[opt] identifier[opt]
+ | "class" identifier[opt] "=" type-id
+ | "typename" "..."[opt] identifier[opt]
+ | "typename" identifier[opt] "=" type-id
+ | "template" "<" template-parameter-list ">"
+ "class" "..."[opt] identifier[opt]
+ | "template" "<" template-parameter-list ">"
+ "class" identifier[opt] "=" id-expression
+ # also, from C++17 we can have "typname" in template templates
+ templateDeclPrefix ->
+ "template" "<" template-parameter-list ">"
+
+ simple-declaration ->
+ 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)
+ | "register"
| type-specifier -> trailing-type-specifier
| function-specifier -> "inline" | "virtual" | "explicit" (only
for function_object)
@@ -93,7 +134,7 @@
declerator ->
ptr-declerator
| noptr-declarator parameters-and-qualifiers trailing-return-type
- (TODO: for now we don't support it)
+ (TODO: for now we don't support trailing-eturn-type)
ptr-declerator ->
noptr-declerator
| ptr-operator ptr-declarator
@@ -104,7 +145,13 @@
| noptr-declerator parameters-and-qualifiers
| noptr-declarator "[" constant-expression[opt] "]"
attribute-specifier-seq[opt]
- | "(" ptr-declarator ")" # TODO: not implemented yet
+ | "(" ptr-declarator ")"
+ ptr-operator ->
+ "*" attribute-specifier-seq[opt] cv-qualifier-seq[opt]
+ | "& attribute-specifier-seq[opt]
+ | "&&" attribute-specifier-seq[opt]
+ | "::"[opt] nested-name-specifier "*" attribute-specifier-seq[opt]
+ cv-qualifier-seq[opt]
# function_object must use a parameters-and-qualifiers, the others may
# use it (e.g., function poitners)
parameters-and-qualifiers ->
@@ -147,25 +194,33 @@
-> decl-specifier-seq abstract-declarator[opt]
grammar, typedef-like: no initilizer
decl-specifier-seq declerator
+ Can start with a templateDeclPrefix.
member_object:
goal: as a type_object which must have a declerator, and optionally
with a initializer
grammar:
decl-specifier-seq declerator initializer
+ Can start with a templateDeclPrefix.
function_object:
goal: a function declaration, TODO: what about templates? for now: skip
grammar: no initializer
decl-specifier-seq declerator
+ Can start with a templateDeclPrefix.
class_object:
goal: a class declaration, but with specification of a base class
- TODO: what about templates? for now: skip
grammar:
- nested-name
- | nested-name ":"
- 'comma-separated list of nested-name optionally with visibility'
+ nested-name "final"[opt] (":" base-specifier-list)[opt]
+ base-specifier-list ->
+ base-specifier "..."[opt]
+ | base-specifier-list, base-specifier "..."[opt]
+ base-specifier ->
+ base-type-specifier
+ | "virtual" access-spe"cifier[opt] base-type-specifier
+ | access-specifier[opt] "virtual"[opt] base-type-specifier
+ Can start with a templateDeclPrefix.
enum_object:
goal: an unscoped enum or a scoped enum, optionally with the underlying
@@ -184,30 +239,11 @@
nested-name
"""
-import re
-from copy import deepcopy
-
-from six import iteritems, text_type
-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.pycompat import UnicodeMixin
-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*\)
@@ -216,10 +252,26 @@ _operator_re = re.compile(r'''(?x)
| (<<|>>)=? | && | \|\|
| [!<>=/*%+|&^~-]=?
''')
-
-#-------------------------------------------------------------------------------
+# see http://en.cppreference.com/w/cpp/keyword
+_keywords = [
+ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor',
+ 'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class',
+ 'compl', 'concept', 'const', 'constexpr', 'const_cast', 'continue',
+ 'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else',
+ 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend',
+ 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
+ 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq',
+ 'private', 'protected', 'public', 'register', 'reinterpret_cast',
+ 'requires', 'return', 'short', 'signed', 'sizeof', 'static',
+ 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
+ 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename',
+ 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t',
+ 'while', 'xor', 'xor_eq'
+]
+
+# ------------------------------------------------------------------------------
# Id v1 constants
-#-------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
_id_fundamental_v1 = {
'char': 'c',
@@ -291,9 +343,9 @@ _id_operator_v1 = {
'[]': 'subscript-operator'
}
-#-------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
# Id v2 constants
-#-------------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
_id_prefix_v2 = '_CPPv2'
_id_fundamental_v2 = {
@@ -387,6 +439,15 @@ _id_operator_v2 = {
}
+class NoOldIdError(UnicodeMixin, Exception):
+ # Used to avoid implementing unneeded id generation for old id schmes.
+ def __init__(self, description=""):
+ self.description = description
+
+ def __unicode__(self):
+ return self.description
+
+
class DefinitionError(UnicodeMixin, Exception):
def __init__(self, description):
self.description = description
@@ -448,10 +509,263 @@ def _verify_description_mode(mode):
raise Exception("Description mode '%s' is invalid." % mode)
+class ASTIdentifier(ASTBase):
+ def __init__(self, identifier):
+ assert identifier is not None
+ self.identifier = identifier
+
+ def get_id_v1(self):
+ if self.identifier == 'size_t':
+ return 's'
+ else:
+ return self.identifier
+
+ def get_id_v2(self):
+ if self.identifier == "std":
+ return 'St'
+ elif self.identifier[0] == "~":
+ # a destructor, just use an arbitrary version of dtors
+ return 'D0'
+ else:
+ return text_type(len(self.identifier)) + self.identifier
+
+ def __unicode__(self):
+ return self.identifier
+
+ def describe_signature(self, signode, mode, env, prefix, symbol):
+ _verify_description_mode(mode)
+ if mode == 'markType':
+ targetText = prefix + self.identifier
+ pnode = addnodes.pending_xref('', refdomain='cpp', reftype='type',
+ reftarget=targetText, modname=None,
+ classname=None)
+ key = symbol.get_lookup_key()
+ assert key
+ pnode['cpp:parentKey'] = key
+ pnode += nodes.Text(self.identifier)
+ signode += pnode
+ elif mode == 'lastIsName':
+ signode += addnodes.desc_name(self.identifier, self.identifier)
+ elif mode == 'noneIsName':
+ signode += nodes.Text(self.identifier)
+ else:
+ raise Exception('Unknown description mode: %s' % mode)
+
+
+class ASTTemplateKeyParamPackIdDefault(ASTBase):
+ def __init__(self, key, identifier, parameterPack, default):
+ assert key
+ if parameterPack:
+ assert default is None
+ self.key = key
+ self.identifier = identifier
+ self.parameterPack = parameterPack
+ self.default = default
+
+ def get_identifier(self):
+ return self.identifier
+
+ def get_id_v2(self):
+ # this is not part of the normal name mangling in C++
+ res = []
+ if self.parameterPack:
+ res.append('Dp')
+ else:
+ res.append('0') # we need to put something
+ return ''.join(res)
+
+ def __unicode__(self):
+ res = [self.key]
+ if self.parameterPack:
+ if self.identifier:
+ res.append(' ')
+ res.append('...')
+ if self.identifier:
+ if not self.parameterPack:
+ res.append(' ')
+ res.append(text_type(self.identifier))
+ if self.default:
+ res.append(' = ')
+ res.append(text_type(self.default))
+ return ''.join(res)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ signode += nodes.Text(self.key)
+ if self.parameterPack:
+ if self.identifier:
+ signode += nodes.Text(' ')
+ signode += nodes.Text('...')
+ if self.identifier:
+ if not self.parameterPack:
+ signode += nodes.Text(' ')
+ self.identifier.describe_signature(signode, mode, env, '', symbol)
+ if self.default:
+ signode += nodes.Text(' = ')
+ self.default.describe_signature(signode, 'markType', env, symbol)
+
+
+class ASTTemplateParamType(ASTBase):
+ def __init__(self, data):
+ assert data
+ self.data = data
+
+ @property
+ def name(self):
+ id = self.get_identifier()
+ return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+
+ def get_identifier(self):
+ return self.data.get_identifier()
+
+ def get_id_v2(self, objectType=None, symbol=None):
+ # this is not part of the normal name mangling in C++
+ if symbol:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id_v2(prefixed=None)
+ else:
+ return self.data.get_id_v2()
+
+ def __unicode__(self):
+ return text_type(self.data)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ self.data.describe_signature(signode, mode, env, symbol)
+
+
+class ASTTemplateParamTemplateType(ASTBase):
+ def __init__(self, nestedParams, data):
+ assert nestedParams
+ assert data
+ self.nestedParams = nestedParams
+ self.data = data
+
+ @property
+ def name(self):
+ id = self.get_identifier()
+ return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+
+ def get_identifier(self):
+ return self.data.get_identifier()
+
+ def get_id_v2(self, objectType=None, symbol=None):
+ # this is not part of the normal name mangling in C++
+ if symbol:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id_v2(prefixed=None)
+ else:
+ return self.nestedParams.get_id_v2() + self.data.get_id_v2()
+
+ def __unicode__(self):
+ return text_type(self.nestedParams) + text_type(self.data)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol)
+ signode += nodes.Text(' ')
+ self.data.describe_signature(signode, mode, env, symbol)
+
+
+class ASTTemplateParamNonType(ASTBase):
+ def __init__(self, param):
+ assert param
+ self.param = param
+
+ @property
+ def name(self):
+ id = self.get_identifier()
+ return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
+
+ def get_identifier(self):
+ name = self.param.name
+ if name:
+ assert len(name.names) == 1
+ assert name.names[0].identifier
+ assert not name.names[0].templateArgs
+ return name.names[0].identifier
+ else:
+ return None
+
+ def get_id_v2(self, objectType=None, symbol=None):
+ # this is not part of the normal name mangling in C++
+ if symbol:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id_v2(prefixed=None)
+ else:
+ return '_' + self.param.get_id_v2()
+
+ def __unicode__(self):
+ return text_type(self.param)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ self.param.describe_signature(signode, mode, env, symbol)
+
+
+class ASTTemplateParams(ASTBase):
+ def __init__(self, params):
+ assert params is not None
+ self.params = params
+
+ def get_id_v2(self):
+ res = []
+ res.append("I")
+ for param in self.params:
+ res.append(param.get_id_v2())
+ res.append("E")
+ return ''.join(res)
+
+ def __unicode__(self):
+ res = []
+ res.append(u"template<")
+ res.append(u", ".join(text_type(a) for a in self.params))
+ res.append(u"> ")
+ return ''.join(res)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ signode += nodes.Text("template<")
+ first = True
+ for param in self.params:
+ if not first:
+ signode += nodes.Text(", ")
+ first = False
+ param.describe_signature(signode, mode, env, symbol)
+ signode += nodes.Text(">")
+
+
+class ASTTemplateDeclarationPrefix(ASTBase):
+ def __init__(self, templates):
+ assert templates is not None
+ assert len(templates) > 0
+ self.templates = templates
+
+ # id_v1 does not exist
+
+ def get_id_v2(self):
+ # this is not part of a normal name mangling system
+ res = []
+ for t in self.templates:
+ res.append(t.get_id_v2())
+ return u''.join(res)
+
+ def __unicode__(self):
+ res = []
+ for t in self.templates:
+ res.append(text_type(t))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ for t in self.templates:
+ templateNode = addnodes.desc_signature()
+ t.describe_signature(templateNode, 'lastIsName', env, symbol)
+ signode += templateNode
+
+
class ASTOperatorBuildIn(ASTBase):
def __init__(self, op):
self.op = op
+ def is_operator(self):
+ return True
+
def get_id_v1(self):
if self.op not in _id_operator_v1:
raise Exception('Internal error: Build-in operator "%s" can not '
@@ -470,10 +784,7 @@ class ASTOperatorBuildIn(ASTBase):
else:
return u'operator' + self.op
- def get_name_no_template(self):
- return text_type(self)
-
- def describe_signature(self, signode, mode, env, prefix):
+ def describe_signature(self, signode, mode, env, prefix, symbol):
_verify_description_mode(mode)
identifier = text_type(self)
if mode == 'lastIsName':
@@ -486,8 +797,8 @@ class ASTOperatorType(ASTBase):
def __init__(self, type):
self.type = type
- def __unicode__(self):
- return u''.join(['operator ', text_type(self.type)])
+ def is_operator(self):
+ return True
def get_id_v1(self):
return u'castto-%s-operator' % self.type.get_id_v1()
@@ -495,10 +806,38 @@ class ASTOperatorType(ASTBase):
def get_id_v2(self):
return u'cv' + self.type.get_id_v2()
+ def __unicode__(self):
+ return u''.join(['operator ', text_type(self.type)])
+
def get_name_no_template(self):
return text_type(self)
- def describe_signature(self, signode, mode, env, prefix):
+ def describe_signature(self, signode, mode, env, prefix, symbol):
+ _verify_description_mode(mode)
+ identifier = text_type(self)
+ if mode == 'lastIsName':
+ signode += addnodes.desc_name(identifier, identifier)
+ else:
+ signode += addnodes.desc_addname(identifier, identifier)
+
+
+class ASTOperatorLiteral(ASTBase):
+ def __init__(self, identifier):
+ self.identifier = identifier
+
+ def is_operator(self):
+ return True
+
+ def get_id_v1(self):
+ raise NoOldIdError()
+
+ def get_id_v2(self):
+ return u'li' + self.identifier.get_id_v2()
+
+ def __unicode__(self):
+ return u'operator""' + text_type(self.identifier)
+
+ def describe_signature(self, signode, mode, env, prefix, symbol):
_verify_description_mode(mode)
identifier = text_type(self)
if mode == 'lastIsName':
@@ -522,25 +861,46 @@ class ASTTemplateArgConstant(ASTBase):
# juse it verbatim for now
return u'X' + text_type(self) + u'E'
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
signode += nodes.Text(text_type(self))
-class ASTNestedNameElementEmpty(ASTBase):
- """Used if a nested name starts with ::"""
+class ASTTemplateArgs(ASTBase):
+ def __init__(self, args):
+ assert args is not None
+ assert len(args) > 0
+ self.args = args
def get_id_v1(self):
- return u''
+ res = []
+ res.append(':')
+ res.append(u'.'.join(a.get_id_v1() for a in self.args))
+ res.append(':')
+ return u''.join(res)
def get_id_v2(self):
- return u''
+ res = []
+ res.append('I')
+ for a in self.args:
+ res.append(a.get_id_v2())
+ res.append('E')
+ return u''.join(res)
def __unicode__(self):
- return u''
+ res = ', '.join(text_type(a) for a in self.args)
+ return '<' + res + '>'
- def describe_signature(self, signode, mode, env, prefix):
- pass
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ signode += nodes.Text('<')
+ first = True
+ for a in self.args:
+ if not first:
+ signode += nodes.Text(', ')
+ first = False
+ a.describe_signature(signode, 'markType', env, symbol=symbol)
+ signode += nodes.Text('>')
class ASTNestedNameElement(ASTBase):
@@ -548,99 +908,58 @@ class ASTNestedNameElement(ASTBase):
self.identifier = identifier
self.templateArgs = templateArgs
+ def is_operator(self):
+ return False
+
def get_id_v1(self):
- res = []
- if self.identifier == 'size_t':
- res.append('s')
- else:
- res.append(self.identifier)
+ res = self.identifier.get_id_v1()
if self.templateArgs:
- res.append(':')
- res.append(u'.'.join(a.get_id_v1() for a in self.templateArgs))
- res.append(':')
- return u''.join(res)
+ res += self.templateArgs.get_id_v1()
+ return res
def get_id_v2(self):
- res = []
- if self.identifier == "std":
- res.append(u'St')
- elif self.identifier[0] == "~":
- # a destructor, just use an arbitrary version of dtors
- res.append("D0")
- else:
- res.append(text_type(len(self.identifier)))
- res.append(self.identifier)
+ res = self.identifier.get_id_v2()
if self.templateArgs:
- res.append('I')
- for a in self.templateArgs:
- res.append(a.get_id_v2())
- res.append('E')
- return u''.join(res)
+ res += self.templateArgs.get_id_v2()
+ return res
def __unicode__(self):
- res = []
- res.append(self.identifier)
+ res = text_type(self.identifier)
if self.templateArgs:
- res.append('<')
- first = True
- for a in self.templateArgs:
- 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)
+ res += text_type(self.templateArgs)
+ return res
- def describe_signature(self, signode, mode, env, prefix):
- _verify_description_mode(mode)
- if mode == 'markType':
- targetText = prefix + text_type(self)
- 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?
- pnode['cpp:parent'] = env.ref_context.get('cpp:parent')
- pnode += nodes.Text(text_type(self.identifier))
- signode += pnode
- elif mode == 'lastIsName':
- name = text_type(self.identifier)
- signode += addnodes.desc_name(name, name)
- else:
- raise Exception('Unknown description mode: %s' % mode)
+ def describe_signature(self, signode, mode, env, prefix, symbol):
+ self.identifier.describe_signature(signode, mode, env, prefix, symbol)
if self.templateArgs:
- signode += nodes.Text('<')
- first = True
- for a in self.templateArgs:
- if not first:
- signode += nodes.Text(', ')
- first = False
- a.describe_signature(signode, 'markType', env)
- signode += nodes.Text('>')
+ self.templateArgs.describe_signature(signode, mode, env, symbol)
class ASTNestedName(ASTBase):
- def __init__(self, names):
+ def __init__(self, names, rooted):
+ assert len(names) > 0
self.names = names
+ self.rooted = rooted
@property
def name(self):
return self
+ def num_templates(self):
+ count = 0
+ for n in self.names:
+ if n.is_operator():
+ continue
+ if n.templateArgs:
+ count += 1
+ return count
+
def get_id_v1(self):
tt = text_type(self)
if tt in _id_shorthands_v1:
return _id_shorthands_v1[tt]
else:
- res = []
- id = self.names[0].get_id_v1()
- if len(id) > 0:
- res.append(id)
- for n in self.names[1:]:
- res.append(n.get_id_v1())
- return u'::'.join(res)
+ return u'::'.join(n.get_id_v1() for n in self.names)
def get_id_v2(self, modifiers=""):
res = []
@@ -653,36 +972,30 @@ class ASTNestedName(ASTBase):
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 += '::'
- 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
- 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])
+ res = []
+ if self.rooted:
+ res.append('')
+ for n in self.names:
+ res.append(text_type(n))
+ return '::'.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
+ # just print the name part, with template args, not template params
if mode == 'lastIsName':
- addname = u'::'.join([text_type(n) for n in self.names[:-1]])
+ addname = []
+ if self.rooted:
+ addname.append('')
+ for n in self.names[:-1]:
+ addname.append(text_type(n))
+ addname = '::'.join(addname)
if len(self.names) > 1:
- addname += u'::'
- name = text_type(self.names[-1])
+ addname += '::'
signode += addnodes.desc_addname(addname, addname)
- self.names[-1].describe_signature(signode, mode, env, '')
+ self.names[-1].describe_signature(signode, mode, env, '', symbol)
elif mode == 'noneIsName':
- name = text_type(self)
- signode += nodes.Text(name)
+ signode += nodes.Text(text_type(self))
elif mode == 'param':
name = text_type(self)
signode += nodes.emphasis(name, name)
@@ -698,7 +1011,7 @@ class ASTNestedName(ASTBase):
prefix += '::'
first = False
if name != '':
- name.describe_signature(signode, mode, env, prefix)
+ name.describe_signature(signode, mode, env, prefix, symbol)
prefix += text_type(name)
else:
raise Exception('Unknown description mode: %s' % mode)
@@ -728,7 +1041,7 @@ class ASTTrailingTypeSpecFundamental(ASTBase):
'parser should have rejected it.' % self.name)
return _id_fundamental_v2[self.name]
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
signode += nodes.Text(text_type(self.name))
@@ -755,11 +1068,11 @@ class ASTTrailingTypeSpecName(ASTBase):
res.append(text_type(self.nestedName))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
if self.prefix:
signode += addnodes.desc_annotation(self.prefix, self.prefix)
signode += nodes.Text(' ')
- self.nestedName.describe_signature(signode, mode, env)
+ self.nestedName.describe_signature(signode, mode, env, symbol=symbol)
class ASTFunctinoParameter(ASTBase):
@@ -785,12 +1098,12 @@ class ASTFunctinoParameter(ASTBase):
else:
return text_type(self.arg)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
if self.ellipsis:
signode += nodes.Text('...')
else:
- self.arg.describe_signature(signode, mode, env)
+ self.arg.describe_signature(signode, mode, env, symbol=symbol)
class ASTParametersQualifiers(ASTBase):
@@ -874,15 +1187,15 @@ class ASTParametersQualifiers(ASTBase):
res.append(self.initializer)
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_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
- arg.describe_signature(param, 'param', env)
+ arg.describe_signature(param, 'param', env, symbol=symbol)
else:
- arg.describe_signature(param, 'markType', env)
+ arg.describe_signature(param, 'markType', env, symbol=symbol)
paramlist += param
signode += paramlist
@@ -910,7 +1223,8 @@ class ASTParametersQualifiers(ASTBase):
class ASTDeclSpecsSimple(ASTBase):
- def __init__(self, storage, inline, virtual, explicit, constexpr, volatile, const):
+ def __init__(self, storage, inline, virtual, explicit,
+ constexpr, volatile, const, friend):
self.storage = storage
self.inline = inline
self.virtual = virtual
@@ -918,6 +1232,7 @@ class ASTDeclSpecsSimple(ASTBase):
self.constexpr = constexpr
self.volatile = volatile
self.const = const
+ self.friend = friend
def mergeWith(self, other):
if not other:
@@ -928,7 +1243,8 @@ class ASTDeclSpecsSimple(ASTBase):
self.explicit or other.explicit,
self.constexpr or other.constexpr,
self.volatile or other.volatile,
- self.const or other.const)
+ self.const or other.const,
+ self.friend or other.friend)
def __unicode__(self):
res = []
@@ -936,6 +1252,8 @@ class ASTDeclSpecsSimple(ASTBase):
res.append(self.storage)
if self.inline:
res.append('inline')
+ if self.friend:
+ res.append('friend')
if self.virtual:
res.append('virtual')
if self.explicit:
@@ -957,6 +1275,8 @@ class ASTDeclSpecsSimple(ASTBase):
_add(modifiers, self.storage)
if self.inline:
_add(modifiers, 'inline')
+ if self.friend:
+ _add(modifiers, 'friend')
if self.virtual:
_add(modifiers, 'virtual')
if self.explicit:
@@ -970,11 +1290,10 @@ class ASTDeclSpecsSimple(ASTBase):
class ASTDeclSpecs(ASTBase):
- def __init__(self, outer, visibility, leftSpecs, rightSpecs, trailing):
+ def __init__(self, outer, leftSpecs, rightSpecs, trailing):
# leftSpecs and rightSpecs are used for output
# allSpecs are used for id generation
self.outer = outer
- self.visibility = visibility
self.leftSpecs = leftSpecs
self.rightSpecs = rightSpecs
self.allSpecs = self.leftSpecs.mergeWith(self.rightSpecs)
@@ -1002,15 +1321,8 @@ class ASTDeclSpecs(ASTBase):
res.append(self.trailingTypeSpec.get_id_v2())
return u''.join(res)
- def _print_visibility(self):
- 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)
l = text_type(self.leftSpecs)
if len(l) > 0:
if len(res) > 0:
@@ -1027,7 +1339,7 @@ class ASTDeclSpecs(ASTBase):
res.append(r)
return "".join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
modifiers = []
@@ -1036,8 +1348,6 @@ class ASTDeclSpecs(ASTBase):
modifiers.append(nodes.Text(' '))
modifiers.append(addnodes.desc_annotation(text, text))
- if self._print_visibility():
- _add(modifiers, self.visibility)
self.leftSpecs.describe_signature(modifiers)
for m in modifiers:
@@ -1045,7 +1355,8 @@ class ASTDeclSpecs(ASTBase):
if self.trailingTypeSpec:
if len(modifiers) > 0:
signode += nodes.Text(' ')
- self.trailingTypeSpec.describe_signature(signode, mode, env)
+ self.trailingTypeSpec.describe_signature(signode, mode, env,
+ symbol=symbol)
modifiers = []
self.rightSpecs.describe_signature(modifiers)
if len(modifiers) > 0:
@@ -1054,82 +1365,387 @@ class ASTDeclSpecs(ASTBase):
signode += m
-class ASTPtrOpPtr(ASTBase):
- def __init__(self, volatile, const):
+class ASTArray(ASTBase):
+ def __init__(self, size):
+ self.size = size
+
+ def __unicode__(self):
+ return u''.join(['[', text_type(self.size), ']'])
+
+ def get_id_v1(self):
+ return u'A'
+
+ def get_id_v2(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 ASTDeclaratorPtr(ASTBase):
+ def __init__(self, next, volatile, const):
+ assert next
+ self.next = next
self.volatile = volatile
self.const = const
+ @property
+ def name(self):
+ return self.next.name
+
+ def require_space_after_declSpecs(self):
+ # TODO: if has paramPack, then False ?
+ return True
+
def __unicode__(self):
res = ['*']
if self.volatile:
- res.append('volatile ')
+ res.append('volatile')
if self.const:
- res.append('const ')
+ if self.volatile:
+ res.append(' ')
+ res.append('const')
+ if self.const or self.volatile:
+ if self.next.require_space_after_declSpecs:
+ res.append(' ')
+ res.append(text_type(self.next))
return u''.join(res)
- def get_id_v1(self):
- res = ['P']
+ # Id v1 ------------------------------------------------------------------
+
+ def get_modifiers_id_v1(self):
+ return self.next.get_modifiers_id_v1()
+
+ def get_param_id_v1(self):
+ return self.next.get_param_id_v1()
+
+ def get_ptr_suffix_id_v1(self):
+ res = 'P'
+ if self.volatile:
+ res += 'V'
+ if self.const:
+ res += 'C'
+ return res + self.next.get_ptr_suffix_id_v1()
+
+ # Id v2 ------------------------------------------------------------------
+
+ def get_modifiers_id_v2(self):
+ return self.next.get_modifiers_id_v2()
+
+ def get_param_id_v2(self):
+ return self.next.get_param_id_v2()
+
+ def get_ptr_suffix_id_v2(self):
+ res = [self.next.get_ptr_suffix_id_v2()]
+ res.append('P')
if self.volatile:
res.append('V')
if self.const:
res.append('C')
return u''.join(res)
- def get_id_v2(self):
+ def get_type_id_v2(self, returnTypeId):
+ # ReturnType *next, so we are part of the return type of 'next
res = ['P']
if self.volatile:
res.append('V')
if self.const:
res.append('C')
- return u''.join(res)
+ res.append(returnTypeId)
+ return self.next.get_type_id_v2(returnTypeId=u''.join(res))
+
+ # ------------------------------------------------------------------------
+
+ def is_function_type(self):
+ return self.next.is_function_type()
+
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ signode += nodes.Text("*")
+
+ def _add_anno(signode, text):
+ signode += addnodes.desc_annotation(text, text)
+ if self.volatile:
+ _add_anno(signode, 'volatile')
+ if self.const:
+ if self.volatile:
+ signode += nodes.Text(' ')
+ _add_anno(signode, 'const')
+ if self.const or self.volatile:
+ if self.next.require_space_after_declSpecs:
+ signode += nodes.Text(' ')
+ self.next.describe_signature(signode, mode, env, symbol)
+
+
+class ASTDeclaratorRef(ASTBase):
+ def __init__(self, next):
+ assert next
+ self.next = next
+ @property
+ def name(self):
+ return self.next.name
+
+ def require_space_after_declSpecs(self):
+ return self.next.require_space_after_declSpecs()
-class ASTPtrOpRef(ASTBase):
def __unicode__(self):
- return '&'
+ return '&' + text_type(self.next)
- def get_id_v1(self):
- return 'R'
+ # Id v1 ------------------------------------------------------------------
- def get_id_v2(self):
- return 'R'
+ def get_modifiers_id_v1(self):
+ return self.next.get_modifiers_id_v1()
+
+ def get_param_id_v1(self): # only the parameters (if any)
+ return self.next.get_param_id_v1()
+
+ def get_ptr_suffix_id_v1(self):
+ return u'R' + self.next.get_ptr_suffix_id_v1()
+
+ # Id v2 ------------------------------------------------------------------
+ def get_modifiers_id_v2(self):
+ return self.next.get_modifiers_id_v2()
+
+ def get_param_id_v2(self): # only the parameters (if any)
+ return self.next.get_param_id_v2()
+
+ def get_ptr_suffix_id_v2(self):
+ return self.next.get_ptr_suffix_id_v2() + u'R'
+
+ def get_type_id_v2(self, returnTypeId):
+ # ReturnType &next, so we are part of the return type of 'next
+ return self.next.get_type_id_v2(returnTypeId=u'R' + returnTypeId)
+
+ # ------------------------------------------------------------------------
+
+ def is_function_type(self):
+ return self.next.is_function_type()
+
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ signode += nodes.Text("&")
+ self.next.describe_signature(signode, mode, env, symbol)
+
+
+class ASTDeclaratorParamPack(ASTBase):
+ def __init__(self, next):
+ assert next
+ self.next = next
+
+ @property
+ def name(self):
+ return self.next.name
+
+ def require_space_after_declSpecs(self):
+ return False
-class ASTPtrOpParamPack(ASTBase):
def __unicode__(self):
- return '...'
+ res = text_type(self.next)
+ if self.next.name:
+ res = ' ' + res
+ return '...' + res
- def get_id_v1(self):
- return 'Dp'
+ # Id v1 ------------------------------------------------------------------
- def get_id_v2(self):
- return 'Dp'
+ def get_modifiers_id_v1(self):
+ return self.next.get_modifiers_id_v1()
+ def get_param_id_v1(self): # only the parameters (if any)
+ return self.next.get_param_id_v1()
-class ASTArray(ASTBase):
- def __init__(self, size):
- self.size = size
+ def get_ptr_suffix_id_v1(self):
+ return 'Dp' + self.next.get_ptr_suffix_id_v2()
+
+ # Id v2 ------------------------------------------------------------------
+
+ def get_modifiers_id_v2(self):
+ return self.next.get_modifiers_id_v2()
+
+ def get_param_id_v2(self): # only the parameters (if any)
+ return self.next.get_param_id_v2()
+
+ def get_ptr_suffix_id_v2(self):
+ return self.next.get_ptr_suffix_id_v2() + u'Dp'
+
+ def get_type_id_v2(self, returnTypeId):
+ # ReturnType... next, so we are part of the return type of 'next
+ return self.next.get_type_id_v2(returnTypeId=u'Dp' + returnTypeId)
+
+ # ------------------------------------------------------------------------
+
+ def is_function_type(self):
+ return self.next.is_function_type()
+
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ signode += nodes.Text("...")
+ if self.next.name:
+ signode += nodes.Text(' ')
+ self.next.describe_signature(signode, mode, env, symbol)
+
+
+class ASTDeclaratorMemPtr(ASTBase):
+ def __init__(self, className, const, volatile, next):
+ assert className
+ assert next
+ self.className = className
+ self.const = const
+ self.volatile = volatile
+ self.next = next
+
+ @property
+ def name(self):
+ return self.next.name
+
+ def require_space_after_declSpecs(self):
+ return True
def __unicode__(self):
- return u''.join(['[', text_type(self.size), ']'])
+ res = []
+ res.append(text_type(self.className))
+ res.append('::*')
+ if self.volatile:
+ res.append(' volatile')
+ if self.const:
+ res.append(' const')
+ if self.next.require_space_after_declSpecs():
+ res.append(' ')
+ res.append(text_type(self.next))
+ return ''.join(res)
- def get_id_v1(self):
- return u'A'
+ # Id v1 ------------------------------------------------------------------
- def get_id_v2(self):
- # TODO: this should maybe be done differently
- return u'A' + text_type(self.size) + u'_'
+ def get_modifiers_id_v1(self):
+ raise NoOldIdError()
- def describe_signature(self, signode, mode, env):
+ def get_param_id_v1(self): # only the parameters (if any)
+ raise NoOldIdError()
+
+ def get_ptr_suffix_id_v1(self):
+ raise NoOldIdError()
+
+ # Id v2 ------------------------------------------------------------------
+
+ def get_modifiers_id_v2(self):
+ return self.next.get_modifiers_id_v2()
+
+ def get_param_id_v2(self): # only the parameters (if any)
+ return self.next.get_param_id_v2()
+
+ def get_ptr_suffix_id_v2(self):
+ raise NotImplementedError()
+ return self.next.get_ptr_suffix_id_v2() + u'Dp'
+
+ def get_type_id_v2(self, returnTypeId):
+ # ReturnType name::* next, so we are part of the return type of next
+ nextReturnTypeId = ''
+ if self.volatile:
+ nextReturnTypeId += 'V'
+ if self.const:
+ nextReturnTypeId += 'K'
+ nextReturnTypeId += 'M'
+ nextReturnTypeId += self.className.get_id_v2()
+ nextReturnTypeId += returnTypeId
+ return self.next.get_type_id_v2(nextReturnTypeId)
+
+ # ------------------------------------------------------------------------
+
+ def is_function_type(self):
+ return self.next.is_function_type()
+
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
- signode += nodes.Text(text_type(self))
+ self.className.describe_signature(signode, mode, env, symbol)
+ signode += nodes.Text('::*')
+
+ def _add_anno(signode, text):
+ signode += addnodes.desc_annotation(text, text)
+ if self.volatile:
+ _add_anno(signode, 'volatile')
+ if self.const:
+ if self.volatile:
+ signode += nodes.Text(' ')
+ _add_anno(signode, 'const')
+ if self.next.require_space_after_declSpecs():
+ if self.volatile or self.const:
+ signode += nodes.Text(' ')
+ self.next.describe_signature(signode, mode, env, symbol)
+
+
+class ASTDeclaratorParen(ASTBase):
+ def __init__(self, inner, next):
+ assert inner
+ assert next
+ self.inner = inner
+ self.next = next
+ # TODO: we assume the name, params, and qualifiers are in inner
+
+ @property
+ def name(self):
+ return self.inner.name
+
+ def require_space_after_declSpecs(self):
+ return True
+
+ def __unicode__(self):
+ res = ['(']
+ res.append(text_type(self.inner))
+ res.append(')')
+ res.append(text_type(self.next))
+ return ''.join(res)
+
+ # Id v1 ------------------------------------------------------------------
+
+ def get_modifiers_id_v1(self):
+ return self.inner.get_modifiers_id_v1()
+
+ def get_param_id_v1(self): # only the parameters (if any)
+ return self.inner.get_param_id_v1()
+
+ def get_ptr_suffix_id_v1(self):
+ raise NoOldIdError() # TODO: was this implemented before?
+ return self.next.get_ptr_suffix_id_v2() + \
+ self.inner.get_ptr_suffix_id_v2()
+
+ # Id v2 ------------------------------------------------------------------
+
+ def get_modifiers_id_v2(self):
+ return self.inner.get_modifiers_id_v2()
+
+ def get_param_id_v2(self): # only the parameters (if any)
+ return self.inner.get_param_id_v2()
+
+ def get_ptr_suffix_id_v2(self):
+ return self.inner.get_ptr_suffix_id_v2() + \
+ self.next.get_ptr_suffix_id_v2()
+
+ def get_type_id_v2(self, returnTypeId):
+ # ReturnType (inner)next, so 'inner' returns everything outside
+ nextId = self.next.get_type_id_v2(returnTypeId)
+ return self.inner.get_type_id_v2(returnTypeId=nextId)
+
+ # ------------------------------------------------------------------------
+ def is_function_type(self):
+ return self.inner.is_function_type()
-class ASTDeclerator(ASTBase):
- def __init__(self, ptrOps, declId, suffixOps):
- self.ptrOps = ptrOps
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ signode += nodes.Text('(')
+ self.inner.describe_signature(signode, mode, env, symbol)
+ signode += nodes.Text(')')
+ self.next.describe_signature(signode, "noneIsName", env, symbol)
+
+
+class ASTDecleratorNameParamQual(ASTBase):
+ def __init__(self, declId, arrayOps, paramQual):
self.declId = declId
- self.suffixOps = suffixOps
+ self.arrayOps = arrayOps
+ self.paramQual = paramQual
@property
def name(self):
@@ -1139,75 +1755,78 @@ class ASTDeclerator(ASTBase):
def get_modifiers_id_v1(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_v1()
+ if self.paramQual:
+ return self.paramQual.get_modifiers_id_v1()
raise Exception(
"This should only be called on a function: %s" % text_type(self))
def get_param_id_v1(self): # only the parameters (if any)
- for op in self.suffixOps:
- if isinstance(op, ASTParametersQualifiers):
- return op.get_param_id_v1()
- return ''
+ if self.paramQual:
+ return self.paramQual.get_param_id_v1()
+ else:
+ return ''
- def get_ptr_suffix_id_v1(self): # only the ptr ops and array specifiers
- return u''.join(
- a.get_id_v1()
- for a in self.ptrOps + self.suffixOps
- if not isinstance(a, ASTParametersQualifiers))
+ def get_ptr_suffix_id_v1(self): # only the array specifiers
+ return u''.join(a.get_id_v1() for a in self.arrayOps)
# Id v2 ------------------------------------------------------------------
def get_modifiers_id_v2(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_v2()
+ if self.paramQual:
+ return self.paramQual.get_modifiers_id_v2()
raise Exception(
"This should only be called on a function: %s" % text_type(self))
def get_param_id_v2(self): # only the parameters (if any)
- for op in self.suffixOps:
- if isinstance(op, ASTParametersQualifiers):
- return op.get_param_id_v2()
- return ''
-
- def get_ptr_suffix_id_v2(self): # only the ptr ops and array specifiers
- return u''.join(
- a.get_id_v2()
- 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
+ if self.paramQual:
+ return self.paramQual.get_param_id_v2()
else:
- return self.declId is not None
+ return ''
+
+ def get_ptr_suffix_id_v2(self): # only the array specifiers
+ return u''.join(a.get_id_v2() for a in self.arrayOps)
+
+ def get_type_id_v2(self, returnTypeId):
+ res = []
+ # TOOD: can we actually have both array ops and paramQual?
+ res.append(self.get_ptr_suffix_id_v2())
+ if self.paramQual:
+ res.append(self.get_modifiers_id_v2())
+ res.append('F')
+ res.append(returnTypeId)
+ res.append(self.get_param_id_v2())
+ res.append('E')
+ else:
+ res.append(returnTypeId)
+ return u''.join(res)
+
+ # ------------------------------------------------------------------------
+
+ def require_space_after_declSpecs(self):
+ return self.declId is not None
+
+ def is_function_type(self):
+ return self.paramQual is not 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 self.declId:
res.append(text_type(self.declId))
- for op in self.suffixOps:
+ for op in self.arrayOps:
res.append(text_type(op))
+ if self.paramQual:
+ res.append(text_type(self.paramQual))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_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:
+ self.declId.describe_signature(signode, mode, env, symbol)
+ for op in self.arrayOps:
op.describe_signature(signode, mode, env)
+ if self.paramQual:
+ self.paramQual.describe_signature(signode, mode, env, symbol)
class ASTInitializer(ASTBase):
@@ -1224,79 +1843,80 @@ class ASTInitializer(ASTBase):
class ASTType(ASTBase):
def __init__(self, declSpecs, decl):
+ assert declSpecs
+ assert 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
return name
- def get_id_v1(self):
+ def get_id_v1(self, objectType=None, symbol=None):
res = []
- if self.objectType: # needs the name
- if self.objectType == 'function': # also modifiers
- res.append(self.name.get_id_v1())
+ if objectType: # needs the name
+ if objectType == 'function': # also modifiers
+ res.append(symbol.get_full_nested_name().get_id_v1())
res.append(self.decl.get_param_id_v1())
res.append(self.decl.get_modifiers_id_v1())
- if (self.declSpecs.leftSpecs.constexpr
- or (self.declSpecs.rightSpecs
- and self.declSpecs.rightSpecs.constexpr)):
+ if (self.declSpecs.leftSpecs.constexpr or
+ (self.declSpecs.rightSpecs and
+ self.declSpecs.rightSpecs.constexpr)):
res.append('CE')
- elif self.objectType == 'type': # just the name
- res.append(self.name.get_id_v1())
+ elif objectType == 'type': # just the name
+ res.append(symbol.get_full_nested_name().get_id_v1())
else:
- print(self.objectType)
+ print(objectType)
assert False
else: # only type encoding
+ if self.decl.is_function_type():
+ raise NoOldIdError()
res.append(self.declSpecs.get_id_v1())
res.append(self.decl.get_ptr_suffix_id_v1())
res.append(self.decl.get_param_id_v1())
return u''.join(res)
- def get_id_v2(self):
+ def get_id_v2(self, objectType=None, symbol=None):
res = []
- if self.objectType: # needs the name
- res.append(_id_prefix_v2)
- if self.objectType == 'function': # also modifiers
+ if objectType: # needs the name
+ if objectType == 'function': # also modifiers
modifiers = self.decl.get_modifiers_id_v2()
- res.append(self.prefixedName.get_id_v2(modifiers))
+ res.append(symbol.get_full_nested_name().get_id_v2(modifiers))
res.append(self.decl.get_param_id_v2())
- elif self.objectType == 'type': # just the name
- res.append(self.prefixedName.get_id_v2())
+ elif objectType == 'type': # just the name
+ res.append(symbol.get_full_nested_name().get_id_v2())
else:
- print(self.objectType)
+ print(objectType)
assert False
else: # only type encoding
- res.append(self.decl.get_ptr_suffix_id_v2())
- res.append(self.declSpecs.get_id_v2())
- res.append(self.decl.get_param_id_v2())
+ # the 'returnType' of a non-function type is simply just the last
+ # type, i.e., for 'int*' it is 'int'
+ returnTypeId = self.declSpecs.get_id_v2()
+ typeId = self.decl.get_type_id_v2(returnTypeId)
+ res.append(typeId)
return u''.join(res)
def __unicode__(self):
res = []
declSpecs = text_type(self.declSpecs)
res.append(declSpecs)
- if self.decl.require_start_space() and len(declSpecs) > 0:
+ if self.decl.require_space_after_declSpecs() and len(declSpecs) > 0:
res.append(u' ')
res.append(text_type(self.decl))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
- self.declSpecs.describe_signature(signode, 'markType', env)
- if (self.decl.require_start_space() and
+ self.declSpecs.describe_signature(signode, 'markType', env, symbol)
+ if (self.decl.require_space_after_declSpecs() and
len(text_type(self.declSpecs)) > 0):
signode += nodes.Text(' ')
- self.decl.describe_signature(signode, mode, env)
+ self.decl.describe_signature(signode, mode, env, symbol)
class ASTTypeWithInit(ASTBase):
def __init__(self, type, init):
- self.objectType = None
self.type = type
self.init = init
@@ -1304,15 +1924,16 @@ class ASTTypeWithInit(ASTBase):
def name(self):
return self.type.name
- def get_id_v1(self):
- if self.objectType == 'member':
- return self.name.get_id_v1() + u'__' + self.type.get_id_v1()
+ def get_id_v1(self, objectType=None, symbol=None):
+ if objectType == 'member':
+ return symbol.get_full_nested_name().get_id_v1() + u'__' \
+ + self.type.get_id_v1()
else:
- return self.type.get_id_v1()
+ return self.type.get_id_v1(objectType)
- def get_id_v2(self):
- if self.objectType == 'member':
- return _id_prefix_v2 + self.prefixedName.get_id_v2()
+ def get_id_v2(self, objectType=None, symbol=None):
+ if objectType == 'member':
+ return symbol.declaration.name.get_id_v2()
else:
return self.type.get_id_v2()
@@ -1323,57 +1944,90 @@ class ASTTypeWithInit(ASTBase):
res.append(text_type(self.init))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
- self.type.describe_signature(signode, mode, env)
+ self.type.describe_signature(signode, mode, env, symbol=symbol)
if self.init:
self.init.describe_signature(signode, mode)
+class ASTTypeUsing(ASTBase):
+ def __init__(self, name, type):
+ self.name = name
+ self.type = type
+
+ def get_id_v1(self, objectType=None, symbol=None):
+ raise NoOldIdError()
+
+ def get_id_v2(self, objectType=None, symbol=None):
+ return symbol.get_full_nested_name().get_id_v2()
+
+ def __unicode__(self):
+ res = []
+ res.append(text_type(self.name))
+ if self.type:
+ res.append(' = ')
+ res.append(text_type(self.type))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ _verify_description_mode(mode)
+ self.name.describe_signature(signode, mode, env, symbol=symbol)
+ if self.type:
+ signode += nodes.Text(' = ')
+ self.type.describe_signature(signode, 'markType', env, symbol=symbol)
+
+
class ASTBaseClass(ASTBase):
- def __init__(self, name, visibility):
+ def __init__(self, name, visibility, virtual, pack):
self.name = name
self.visibility = visibility
+ self.virtual = virtual
+ self.pack = pack
def __unicode__(self):
res = []
if self.visibility != 'private':
res.append(self.visibility)
res.append(' ')
+ if self.virtual:
+ res.append('virtual ')
res.append(text_type(self.name))
+ if self.pack:
+ res.append('...')
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_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(' ')
+ if self.virtual:
+ signode += addnodes.desc_annotation('virtual', 'virtual')
signode += nodes.Text(' ')
- self.name.describe_signature(signode, mode, env)
+ self.name.describe_signature(signode, 'markType', env, symbol=symbol)
+ if self.pack:
+ signode += nodes.Text('...')
class ASTClass(ASTBase):
- def __init__(self, name, visibility, bases):
+ def __init__(self, name, final, bases):
self.name = name
- self.visibility = visibility
+ self.final = final
self.bases = bases
- def get_id_v1(self):
- return self.name.get_id_v1()
- #name = _id_shortwords.get(self.name)
- #if name is not None:
- # return name
- #return self.name.replace(u' ', u'-')
+ def get_id_v1(self, objectType, symbol):
+ return symbol.get_full_nested_name().get_id_v1()
- def get_id_v2(self):
- return _id_prefix_v2 + self.prefixedName.get_id_v2()
+ def get_id_v2(self, objectType, symbol):
+ return symbol.get_full_nested_name().get_id_v2()
def __unicode__(self):
res = []
- if self.visibility != 'public':
- res.append(self.visibility)
- res.append(' ')
res.append(text_type(self.name))
+ if self.final:
+ res.append(' final')
if len(self.bases) > 0:
res.append(' : ')
first = True
@@ -1384,69 +2038,63 @@ class ASTClass(ASTBase):
res.append(text_type(b))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
- if self.visibility != 'public':
- signode += addnodes.desc_annotation(
- self.visibility, self.visibility)
+ self.name.describe_signature(signode, mode, env, symbol=symbol)
+ if self.final:
signode += nodes.Text(' ')
- self.name.describe_signature(signode, mode, env)
+ signode += addnodes.desc_annotation('final', 'final')
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, symbol=symbol)
signode += nodes.Text(', ')
signode.pop()
+
class ASTEnum(ASTBase):
- def __init__(self, name, visibility, scoped, underlyingType):
+ def __init__(self, name, scoped, underlyingType):
self.name = name
- self.visibility = visibility
self.scoped = scoped
self.underlyingType = underlyingType
- def get_id_v1(self):
- return None # did not exist at that time
+ def get_id_v1(self, objectType, symbol):
+ raise NoOldIdError()
- def get_id_v2(self):
- return _id_prefix_v2 + self.prefixedName.get_id_v2()
+ def get_id_v2(self, objectType, symbol):
+ return symbol.get_full_nested_name().get_id_v2()
def __unicode__(self):
res = []
if self.scoped:
res.append(self.scoped)
res.append(' ')
- if self.visibility != 'public':
- res.append(self.visibility)
- res.append(' ')
res.append(text_type(self.name))
if self.underlyingType:
res.append(' : ')
res.append(text_type(self.underlyingType))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
# self.scoped has been done by the CPPEnumObject
- if self.visibility != 'public':
- signode += addnodes.desc_annotation(
- self.visibility, self.visibility)
- signode += nodes.Text(' ')
- self.name.describe_signature(signode, mode, env)
+ self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.underlyingType:
signode += nodes.Text(' : ')
- self.underlyingType.describe_signature(signode, 'noneIsName', env)
+ self.underlyingType.describe_signature(signode, 'noneIsName',
+ env, symbol=symbol)
+
class ASTEnumerator(ASTBase):
def __init__(self, name, init):
self.name = name
self.init = init
- def get_id_v1(self):
- return None # did not exist at that time
+ def get_id_v1(self, objectType, symbol):
+ raise NoOldIdError()
- def get_id_v2(self):
- return _id_prefix_v2 + self.prefixedName.get_id_v2()
+ def get_id_v2(self, objectType, symbol):
+ return symbol.get_full_nested_name().get_id_v2()
def __unicode__(self):
res = []
@@ -1455,13 +2103,530 @@ class ASTEnumerator(ASTBase):
res.append(text_type(self.init))
return u''.join(res)
- def describe_signature(self, signode, mode, env):
+ def describe_signature(self, signode, mode, env, symbol):
_verify_description_mode(mode)
- self.name.describe_signature(signode, mode, env)
+ self.name.describe_signature(signode, mode, env, symbol=symbol)
if self.init:
self.init.describe_signature(signode, 'noneIsName')
+class ASTDeclaration(ASTBase):
+ def __init__(self, objectType, visibility, templatePrefix, declaration):
+ self.objectType = objectType
+ self.visibility = visibility
+ self.templatePrefix = templatePrefix
+ self.declaration = declaration
+
+ self.symbol = None
+ # set by CPPObject._add_enumerator_to_parent
+ self.enumeratorScopedSymbol = None
+
+ def clone(self):
+ if self.templatePrefix:
+ templatePrefixClone = self.templatePrefix.clone()
+ else:
+ templatePrefixClone = None
+ return ASTDeclaration(self.objectType, self.visibility,
+ templatePrefixClone,
+ self.declaration.clone())
+
+ @property
+ def name(self):
+ return self.declaration.name
+
+ def get_id_v1(self):
+ if self.templatePrefix:
+ raise NoOldIdError()
+ if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
+ return self.enumeratorScopedSymbol.declaration.get_id_v1()
+ return self.declaration.get_id_v1(self.objectType, self.symbol)
+
+ def get_id_v2(self, prefixed=True):
+ if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
+ return self.enumeratorScopedSymbol.declaration.get_id_v2(prefixed)
+ if prefixed:
+ res = [_id_prefix_v2]
+ else:
+ res = []
+ if self.templatePrefix:
+ res.append(self.templatePrefix.get_id_v2())
+ res.append(self.declaration.get_id_v2(self.objectType, self.symbol))
+ return u''.join(res)
+
+ def get_newest_id(self):
+ return self.get_id_v2()
+
+ def __unicode__(self):
+ res = []
+ if self.visibility and self.visibility != "public":
+ res.append(self.visibility)
+ res.append(u' ')
+ if self.templatePrefix:
+ res.append(text_type(self.templatePrefix))
+ res.append(text_type(self.declaration))
+ return u''.join(res)
+
+ def describe_signature(self, signode, mode, env):
+ _verify_description_mode(mode)
+ # the caller of the domain added a desc_signature node
+ # let's pop it so we can add templates before that
+ parentNode = signode.parent
+ mainDeclNode = signode
+ parentNode.pop()
+
+ assert self.symbol
+ if self.templatePrefix:
+ self.templatePrefix.describe_signature(parentNode, mode, env,
+ symbol=self.symbol)
+ if self.visibility and self.visibility != "public":
+ mainDeclNode += addnodes.desc_annotation(self.visibility + " ",
+ self.visibility + " ")
+ if self.objectType == 'type':
+ mainDeclNode += addnodes.desc_annotation('type ', 'type ')
+ elif self.objectType == 'member':
+ pass
+ elif self.objectType == 'function':
+ pass
+ elif self.objectType == 'class':
+ mainDeclNode += addnodes.desc_annotation('class ', 'class ')
+ elif self.objectType == 'enum':
+ prefix = 'enum '
+ if self.scoped:
+ prefix += self.scoped
+ prefix += ' '
+ mainDeclNode += addnodes.desc_annotation(prefix, prefix)
+ elif self.objectType == 'enumerator':
+ mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
+ else:
+ assert False
+ self.declaration.describe_signature(mainDeclNode, mode, env,
+ symbol=self.symbol)
+ parentNode += mainDeclNode
+
+
+class ASTNamespace(ASTBase):
+ def __init__(self, nestedName, templatePrefix):
+ self.nestedName = nestedName
+ self.templatePrefix = templatePrefix
+
+
+class Symbol(object):
+ def _assert_invariants(self):
+ if not self.parent:
+ # parent == None means global scope, so declaration means a parent
+ assert not self.identifier
+ assert not self.templateParams
+ assert not self.templateArgs
+ assert not self.declaration
+ assert not self.docname
+ else:
+ if not self.identifier:
+ # in case it's an operator
+ assert self.declaration
+ if self.declaration:
+ assert self.docname
+
+ def __init__(self, parent, identifier,
+ templateParams, templateArgs, declaration, docname):
+ self.parent = parent
+ self.identifier = identifier
+ self.templateParams = templateParams # template<templateParams>
+ self.templateArgs = templateArgs # identifier<templateArgs>
+ self.declaration = declaration
+ self.docname = docname
+ self._assert_invariants()
+
+ self.children = []
+ if self.parent:
+ self.parent.children.append(self)
+ if self.declaration:
+ self.declaration.symbol = self
+ # add symbols for the template params
+ # (do it after self.children has been initialised
+ if self.templateParams:
+ for p in self.templateParams.params:
+ if not p.get_identifier():
+ continue
+ # only add a declaration if we our selfs from a declaration
+ if declaration:
+ decl = ASTDeclaration('templateParam', None, None, p)
+ else:
+ decl = None
+ nne = ASTNestedNameElement(p.get_identifier(), None)
+ nn = ASTNestedName([nne], rooted=False)
+ self._add_symbols(nn, [], decl, docname)
+
+ def _fill_empty(self, declaration, docname):
+ self._assert_invariants()
+ assert not self.declaration
+ assert not self.docname
+ assert declaration
+ assert docname
+ self.declaration = declaration
+ self.declaration.symbol = self
+ self.docname = docname
+ self._assert_invariants()
+
+ def clear_doc(self, docname):
+ for sChild in self.children:
+ sChild.clear_doc(docname)
+ if sChild.declaration and sChild.docname == docname:
+ sChild.declaration = None
+ sChild.docname = None
+
+ def get_all_symbols(self):
+ yield self
+ for sChild in self.children:
+ for s in sChild.get_all_symbols():
+ yield s
+
+ def get_lookup_key(self):
+ if not self.parent:
+ # specialise for the root
+ return None
+ symbols = []
+ s = self
+ while s.parent:
+ symbols.append(s)
+ s = s.parent
+ symbols.reverse()
+ key = []
+ for s in symbols:
+ if s.identifier:
+ nne = ASTNestedNameElement(s.identifier, s.templateArgs)
+ else:
+ assert s.declaration
+ nne = s.declaration.name.names[-1]
+ key.append((nne, s.templateParams))
+ return key
+
+ def get_full_nested_name(self):
+ names = []
+ for nne, templateParams in self.get_lookup_key():
+ names.append(nne)
+ return ASTNestedName(names, rooted=False)
+
+ def _find_named_symbol(self, identifier, templateParams,
+ templateArgs, operator,
+ templateShorthand, matchSelf):
+ assert (identifier is None) != (operator is None)
+
+ def matches(s):
+ if s.identifier != identifier:
+ return False
+ if not s.identifier:
+ if not s.declaration:
+ return False
+ assert operator
+ name = s.declaration.name.names[-1]
+ if not name.is_operator():
+ return False
+ if text_type(name) != text_type(operator):
+ return False
+ if (s.templateParams is None) != (templateParams is None):
+ if templateParams is not None:
+ # we query with params, they must match params
+ return False
+ if not templateShorthand:
+ # we don't query with params, and we do care about them
+ return False
+ if templateParams:
+ # TODO: do better comparison
+ if text_type(s.templateParams) != text_type(templateParams):
+ return False
+ if (s.templateArgs is None) != (templateArgs is None):
+ return False
+ if s.templateArgs:
+ # TODO: do better comparison
+ if text_type(s.templateArgs) != text_type(templateArgs):
+ return False
+ return True
+ if matchSelf and matches(self):
+ return self
+ for s in self.children:
+ if matches(s):
+ return s
+ return None
+
+ def _add_symbols(self, nestedName, templateDecls, declaration, docname):
+ # This condition should be checked at the parser level.
+ # Each template argument list must have a template parameter list.
+ # But to declare a template there must be an additional template parameter list.
+ assert(nestedName.num_templates() == len(templateDecls) or
+ nestedName.num_templates() + 1 == len(templateDecls))
+
+ parentSymbol = self
+ if nestedName.rooted:
+ while parentSymbol.parent:
+ parentSymbol = parentSymbol.parent
+ names = nestedName.names
+ iTemplateDecl = 0
+ for name in names[:-1]:
+ # there shouldn't be anything inside an operator
+ # (other than template parameters, which are not added this way, right?)
+ assert not name.is_operator()
+ identifier = name.identifier
+ templateArgs = name.templateArgs
+ if templateArgs:
+ assert iTemplateDecl < len(templateDecls)
+ templateParams = templateDecls[iTemplateDecl]
+ iTemplateDecl += 1
+ else:
+ templateParams = None
+ symbol = parentSymbol._find_named_symbol(identifier,
+ templateParams,
+ templateArgs,
+ operator=None,
+ templateShorthand=False,
+ matchSelf=False)
+ if symbol is None:
+ symbol = Symbol(parent=parentSymbol, identifier=identifier,
+ templateParams=templateParams,
+ templateArgs=templateArgs, declaration=None,
+ docname=None)
+ parentSymbol = symbol
+ name = names[-1]
+ if name.is_operator():
+ identifier = None
+ templateArgs = None
+ operator = name
+ else:
+ identifier = name.identifier
+ templateArgs = name.templateArgs
+ operator = None
+ if iTemplateDecl < len(templateDecls):
+ if iTemplateDecl + 1 != len(templateDecls):
+ print(text_type(templateDecls))
+ print(text_type(nestedName))
+ assert iTemplateDecl + 1 == len(templateDecls)
+ templateParams = templateDecls[iTemplateDecl]
+ else:
+ assert iTemplateDecl == len(templateDecls)
+ templateParams = None
+ symbol = parentSymbol._find_named_symbol(identifier,
+ templateParams,
+ templateArgs,
+ operator,
+ templateShorthand=False,
+ matchSelf=False)
+ if symbol:
+ if not declaration:
+ # good, just a scope creation
+ return symbol
+ if not symbol.declaration:
+ # If someone first opened the scope, and then later
+ # declares it, e.g,
+ # .. namespace:: Test
+ # .. namespace:: nullptr
+ # .. class:: Test
+ symbol._fill_empty(declaration, docname)
+ return symbol
+ # it may simply be a functin overload
+ # TODO: it could be a duplicate but let's just insert anyway
+ # the id generation will warn about it
+ symbol = Symbol(parent=parentSymbol, identifier=identifier,
+ templateParams=templateParams,
+ templateArgs=templateArgs,
+ declaration=declaration,
+ docname=docname)
+ else:
+ symbol = Symbol(parent=parentSymbol, identifier=identifier,
+ templateParams=templateParams,
+ templateArgs=templateArgs,
+ declaration=declaration,
+ docname=docname)
+ return symbol
+
+ def merge_with(self, other, docnames, env):
+ assert other is not None
+ for otherChild in other.children:
+ if not otherChild.identifier:
+ if not otherChild.declaration:
+ print("WTF?")
+ print(otherChild.dump(0))
+ print(other.dump(0))
+ assert otherChild.declaration
+ operator = otherChild.declaration.name.names[-1]
+ assert operator.is_operator()
+ else:
+ operator = None
+ ourChild = self._find_named_symbol(otherChild.identifier,
+ otherChild.templateParams,
+ otherChild.templateArgs,
+ operator,
+ templateShorthand=False,
+ matchSelf=False)
+ if ourChild is None:
+ # TODO: hmm, should we prune by docnames?
+ self.children.append(otherChild)
+ otherChild.parent = self
+ otherChild._assert_invariants()
+ continue
+ if otherChild.declaration and otherChild.docname in docnames:
+ if not ourChild.declaration:
+ ourChild._fill_empty(otherChild.declaration, otherChild.docname)
+ elif ourChild.docname != otherChild.docname:
+ name = text_type(ourChild.declaration)
+ msg = "Duplicate declaration, also defined in '%s'.\n"
+ msg += "Declaration is '%s'."
+ msg = msg % (ourChild.docname, name)
+ env.warn(otherChild.docname, msg)
+ else:
+ # Both have declarations, and in the same docname.
+ # This can apparently happen, it should be safe to
+ # just ignore it, right?
+ pass
+ ourChild.merge_with(otherChild, docnames, env)
+
+ def add_name(self, nestedName, templatePrefix=None):
+ if templatePrefix:
+ templateDecls = templatePrefix.templates
+ else:
+ templateDecls = []
+ return self._add_symbols(nestedName, templateDecls,
+ declaration=None, docname=None)
+
+ def add_declaration(self, declaration, docname):
+ assert declaration
+ assert docname
+ nestedName = declaration.name
+ if declaration.templatePrefix:
+ templateDecls = declaration.templatePrefix.templates
+ else:
+ templateDecls = []
+ return self._add_symbols(nestedName, templateDecls, declaration, docname)
+
+ def find_identifier(self, identifier, matchSelf):
+ if matchSelf and self.identifier and self.identifier == identifier:
+ return self
+ for s in self.children:
+ if s.identifier and s.identifier == identifier:
+ return s
+ return None
+
+ def direct_lookup(self, key):
+ s = self
+ for name, templateParams in key:
+ if name.is_operator():
+ identifier = None
+ templateArgs = None
+ operator = name
+ else:
+ identifier = name.identifier
+ templateArgs = name.templateArgs
+ operator = None
+ s = s._find_named_symbol(identifier, templateParams,
+ templateArgs, operator,
+ templateShorthand=False,
+ matchSelf=True)
+ if not s:
+ return None
+ return s
+
+ def find_name(self, nestedName, templateDecls, templateShorthand, matchSelf):
+ # templateShorthand: missing template parameter lists for templates is ok
+
+ # TODO: unify this with the _add_symbols
+ # This condition should be checked at the parser level.
+ assert len(templateDecls) <= nestedName.num_templates() + 1
+ parentSymbol = self
+ if nestedName.rooted:
+ while parentSymbol.parent:
+ parentSymbol = parentSymbol.parent
+ names = nestedName.names
+
+ # walk up until we find the first identifier
+ firstName = names[0]
+ if not firstName.is_operator():
+ while parentSymbol.parent:
+ if parentSymbol.find_identifier(firstName.identifier,
+ matchSelf=matchSelf):
+ break
+ parentSymbol = parentSymbol.parent
+
+ iTemplateDecl = 0
+ for iName in range(len(names)):
+ name = names[iName]
+ if iName + 1 == len(names):
+ if name.is_operator():
+ identifier = None
+ templateArgs = None
+ operator = name
+ else:
+ identifier = name.identifier
+ templateArgs = name.templateArgs
+ operator = None
+ if iTemplateDecl < len(templateDecls):
+ assert iTemplateDecl + 1 == len(templateDecls)
+ templateParams = templateDecls[iTemplateDecl]
+ else:
+ assert iTemplateDecl == len(templateDecls)
+ templateParams = None
+ symbol = parentSymbol._find_named_symbol(identifier,
+ templateParams,
+ templateArgs,
+ operator,
+ templateShorthand=templateShorthand,
+ matchSelf=matchSelf)
+ if symbol:
+ return symbol
+ else:
+ return None
+ else:
+ # there shouldn't be anything inside an operator
+ assert not name.is_operator()
+ identifier = name.identifier
+ templateArgs = name.templateArgs
+ if templateArgs and iTemplateDecl < len(templateDecls):
+ templateParams = templateDecls[iTemplateDecl]
+ iTemplateDecl += 1
+ else:
+ templateParams = None
+ symbol = parentSymbol._find_named_symbol(identifier,
+ templateParams,
+ templateArgs,
+ operator=None,
+ templateShorthand=templateShorthand,
+ matchSelf=matchSelf)
+ if symbol is None:
+ # TODO: maybe search without template args
+ return None
+ parentSymbol = symbol
+ assert False # should have returned in the loop
+
+ def to_string(self, indent):
+ self._assert_invariants()
+ res = ['\t'*indent]
+ if not self.parent:
+ res.append('::')
+ else:
+ if self.templateParams:
+ res.append(text_type(self.templateParams))
+ res.append('\n')
+ res.append('\t'*indent)
+ if self.identifier:
+ res.append(text_type(self.identifier))
+ else:
+ res.append(text_type(self.declaration))
+ if self.templateArgs:
+ res.append(text_type(self.templateArgs))
+ if self.declaration:
+ res.append(": ")
+ res.append(text_type(self.declaration))
+ if self.docname:
+ res.append('\t(')
+ res.append(self.docname)
+ res.append(')')
+ res.append('\n')
+ return ''.join(res)
+
+ def dump(self, indent):
+ res = [self.to_string(indent)]
+ for c in self.children:
+ res.append(c.dump(indent + 1))
+ return ''.join(res)
+
+
class DefinitionParser(object):
# those without signedness and size modifiers
# see http://en.cppreference.com/w/cpp/language/types
@@ -1470,21 +2635,53 @@ class DefinitionParser(object):
'float', 'double', 'auto'
)
- _prefix_keys = ('class', 'struct', 'union', 'typename')
+ _prefix_keys = ('class', 'struct', 'enum', 'union', 'typename')
- def __init__(self, definition):
+ def __init__(self, definition, warnEnv):
self.definition = definition.strip()
self.pos = 0
self.end = len(self.definition)
self.last_match = None
self._previous_state = (0, None)
+ self.warnEnv = warnEnv
+
+ def _make_multi_error(self, errors, header):
+ if len(errors) == 1:
+ return DefinitionError(header + '\n' + errors[0][0].description)
+ result = [header, '\n']
+ for e in errors:
+ if len(e[1]) > 0:
+ ident = ' '
+ result.append(e[1])
+ result.append(':\n')
+ for line in e[0].description.split('\n'):
+ if len(line) == 0:
+ continue
+ result.append(ident)
+ result.append(line)
+ result.append('\n')
+ else:
+ result.append(e[0].description)
+ return DefinitionError(''.join(result))
+
+ def status(self, msg):
+ # for debugging
+ indicator = '-' * self.pos + '^'
+ print("%s\n%s\n%s" % (msg, self.definition, indicator))
+
def fail(self, msg):
indicator = '-' * self.pos + '^'
raise DefinitionError(
'Invalid definition: %s [error at %d]\n %s\n %s' %
(msg, self.pos, self.definition, indicator))
+ def warn(self, msg):
+ if self.warnEnv:
+ self.warnEnv.warn(msg)
+ else:
+ print("Warning: %s" % msg)
+
def match(self, regex):
match = regex.match(self.definition, self.pos)
if match is not None:
@@ -1540,8 +2737,33 @@ class DefinitionParser(object):
def assert_end(self):
self.skip_ws()
if not self.eof:
- self.fail('expected end of definition, got %r' %
- self.definition[self.pos:])
+ self.fail('Expected end of definition.')
+
+ def _parse_expression(self, end):
+ # Stupidly "parse" an expression.
+ # 'end' should be a list of characters which ends the expression.
+ assert end
+ self.skip_ws()
+ startPos = self.pos
+ if self.match(_string_re):
+ value = self.matched_text
+ else:
+ # TODO: add handling of more bracket-like things, and quote handling
+ brackets = {'(': ')', '[': ']'}
+ symbols = []
+ while not self.eof:
+ if (len(symbols) == 0 and self.current_char in end):
+ break
+ if self.current_char in brackets.keys():
+ symbols.append(brackets[self.current_char])
+ elif len(symbols) > 0 and self.current_char == symbols[-1]:
+ symbols.pop()
+ self.pos += 1
+ if self.eof:
+ self.fail("Could not find end of expression starting at %d."
+ % startPos)
+ value = self.definition[startPos:self.pos].strip()
+ return value.strip()
def _parse_operator(self):
self.skip_ws()
@@ -1562,74 +2784,96 @@ class DefinitionParser(object):
op += '[]'
return ASTOperatorBuildIn(op)
+ # user-defined literal?
+ if self.skip_string('""'):
+ self.skip_ws()
+ if not self.match(_identifier_re):
+ self.fail("Expected user-defined literal suffix.")
+ identifier = ASTIdentifier(self.matched_text)
+ return ASTOperatorLiteral(identifier)
+
# oh well, looks like a cast operator definition.
# In that case, eat another type.
- type = self._parse_type()
+ type = self._parse_type(named=False, outer="operatorCast")
return ASTOperatorType(type)
- def _parse_nested_name(self):
+ def _parse_template_argument_list(self):
+ self.skip_ws()
+ if not self.skip_string('<'):
+ return None
+ prevErrors = []
+ templateArgs = []
+ while 1:
+ pos = self.pos
+ parsedComma = False
+ parsedEnd = False
+ try:
+ type = self._parse_type(named=False)
+ self.skip_ws()
+ if self.skip_string('>'):
+ parsedEnd = True
+ elif self.skip_string(','):
+ parsedComma = True
+ else:
+ self.fail('Expected ">" or "," in template argument list.')
+ templateArgs.append(type)
+ except DefinitionError as e:
+ prevErrors.append((e, "If type argument"))
+ self.pos = pos
+ try:
+ value = self._parse_expression(end=[',', '>'])
+ self.skip_ws()
+ if self.skip_string('>'):
+ parsedEnd = True
+ elif self.skip_string(','):
+ parsedComma = True
+ else:
+ self.fail('Expected ">" or "," in template argument list.')
+ templateArgs.append(ASTTemplateArgConstant(value))
+ except DefinitionError as e:
+ self.pos = pos
+ prevErrors.append((e, "If non-type argument"))
+ header = "Error in parsing template argument list."
+ raise self._make_multi_error(prevErrors, header)
+ if parsedEnd:
+ assert not parsedComma
+ break
+ return ASTTemplateArgs(templateArgs)
+
+ def _parse_nested_name(self, memberPointer=False):
names = []
self.skip_ws()
+ rooted = False
if self.skip_string('::'):
- names.append(ASTNestedNameElementEmpty())
+ rooted = True
while 1:
self.skip_ws()
- # TODO: parse the "template" keyword
- if not self.match(_identifier_re):
- self.fail("expected identifier")
- identifier = self.matched_text
- if identifier == 'operator':
+ if self.skip_word_and_ws('template'):
+ self.fail("'template' in nested name not implemented.")
+ elif self.skip_word_and_ws('operator'):
op = self._parse_operator()
names.append(op)
else:
- templateArgs = None
- self.skip_ws()
- if self.skip_string('<'):
- templateArgs = []
- while 1:
- pos = self.pos
- try:
- type = self._parse_type(allowParams=True)
- templateArgs.append(type)
- except DefinitionError:
- self.pos = pos
- symbols = []
- startPos = self.pos
- self.skip_ws()
- if self.match(_string_re):
- 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
- 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()
- templateArgs.append(ASTTemplateArgConstant(value))
- self.skip_ws()
- if self.skip_string('>'):
- break
- elif self.skip_string(','):
- continue
- else:
- self.fail('Expected ">" or "," in template '
- 'argument list.')
+ if not self.match(_identifier_re):
+ if memberPointer and len(names) > 0:
+ break
+ self.fail("Expected identifier in nested name.")
+ identifier = self.matched_text
+ # make sure there isn't a keyword
+ if identifier in _keywords:
+ self.fail("Expected identifier in nested name, "
+ "got keyword: %s" % identifier)
+ templateArgs = self._parse_template_argument_list()
+ identifier = ASTIdentifier(identifier)
names.append(ASTNestedNameElement(identifier, templateArgs))
self.skip_ws()
if not self.skip_string('::'):
+ if memberPointer:
+ self.fail("Expected '::' in pointer to member (function).")
break
- return ASTNestedName(names)
+ return ASTNestedName(names, rooted)
def _parse_trailing_type_spec(self):
# fundemental types
@@ -1640,7 +2884,6 @@ class DefinitionParser(object):
# 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'):
@@ -1652,7 +2895,9 @@ class DefinitionParser(object):
elements.append('long')
else:
break
- if self.skip_word_and_ws('int'):
+ if self.skip_word_and_ws('char'):
+ elements.append('char')
+ elif self.skip_word_and_ws('int'):
elements.append('int')
elif self.skip_word_and_ws('double'):
elements.append('double')
@@ -1695,10 +2940,10 @@ class DefinitionParser(object):
'parameters_and_qualifiers.')
break
if paramMode == 'function':
- arg = self._parse_type_with_init(named='maybe')
+ arg = self._parse_type_with_init(outer=None, named='single')
else:
- arg = self._parse_type()
- # TODO: parse default parameters
+ arg = self._parse_type(named=False)
+ # TODO: parse default parameters # TODO: didn't we just do that?
args.append(ASTFunctinoParameter(arg))
self.skip_ws()
@@ -1711,7 +2956,10 @@ class DefinitionParser(object):
'Expecting "," or ")" in parameters_and_qualifiers, '
'got "%s".' % self.current_char)
- if paramMode != 'function':
+ # TODO: why did we have this bail-out?
+ # does it hurt to parse the extra stuff?
+ # it's needed for pointer to member functions
+ if paramMode != 'function' and False:
return ASTParametersQualifiers(
args, None, None, None, None, None, None, None)
@@ -1771,6 +3019,7 @@ class DefinitionParser(object):
constexpr = None
volatile = None
const = None
+ friend = None
while 1: # accept any permutation of a subset of some decl-specs
self.skip_ws()
if not storage:
@@ -1782,11 +3031,9 @@ class DefinitionParser(object):
if self.skip_word('mutable'):
storage = 'mutable'
continue
- if outer == 'fuction':
- # TODO: maybe in more contexts, missing test cases
- if self.skip_word('register'):
- storage = 'register'
- continue
+ if self.skip_word('register'):
+ storage = 'register'
+ continue
if outer == 'function':
# function-specifiers
@@ -1794,6 +3041,10 @@ class DefinitionParser(object):
inline = self.skip_word('inline')
if inline:
continue
+ if not friend:
+ friend = self.skip_word('friend')
+ if friend:
+ continue
if not virtual:
virtual = self.skip_word('virtual')
if virtual:
@@ -1817,100 +3068,173 @@ class DefinitionParser(object):
continue
break
return ASTDeclSpecsSimple(storage, inline, virtual, explicit, constexpr,
- volatile, const)
+ volatile, const, friend)
def _parse_decl_specs(self, outer, typed=True):
+ if outer:
+ if outer not in ('type', 'member', 'function', 'templateParam'):
+ raise Exception('Internal error, unknown outer "%s".' % outer)
"""
- visibility storage-class-specifier function-specifier "constexpr"
+ storage-class-specifier function-specifier "constexpr"
"volatile" "const" trailing-type-specifier
- storage-class-specifier -> "static" (only for member_object and
- function_object)
+ storage-class-specifier ->
+ "static" (only for member_object and function_object)
+ | "register"
function-specifier -> "inline" | "virtual" | "explicit" (only for
function_object)
"constexpr" (only for member_object and function_object)
"""
- visibility = None
- leftSepcs = None
- rightSpecs = None
- if outer:
- self.skip_ws()
- if self.match(_visibility_re):
- visibility = self.matched_text
leftSpecs = self._parse_decl_specs_simple(outer, typed)
+ rightSpecs = None
if typed:
trailing = self._parse_trailing_type_spec()
rightSpecs = self._parse_decl_specs_simple(outer, typed)
else:
trailing = None
- return ASTDeclSpecs(outer, visibility, leftSpecs, rightSpecs, trailing)
-
- def _parse_declerator(self, named, paramMode=None, typed=True):
- if paramMode:
- if paramMode not in ('type', 'function'):
- raise Exception(
- "Internal error, unknown paramMode '%s'." % paramMode)
- ptrOps = []
- while 1:
- 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(ASTPtrOpParamPack())
- break
- else:
- break
+ return ASTDeclSpecs(outer, leftSpecs, rightSpecs, trailing)
+ def _parse_declarator_name_param_qual(self, named, paramMode, typed):
+ # now we should parse the name, and then suffixes
if named == 'maybe':
+ pos = self.pos
try:
declId = self._parse_nested_name()
except DefinitionError:
+ self.pos = pos
+ declId = None
+ elif named == 'single':
+ if self.match(_identifier_re):
+ identifier = ASTIdentifier(self.matched_text)
+ nne = ASTNestedNameElement(identifier, None)
+ declId = ASTNestedName([nne], rooted=False)
+ # if it's a member pointer, we may have '::', which should be an error
+ self.skip_ws()
+ if self.current_char == ':':
+ self.fail("Unexpected ':' after identifier.")
+ else:
declId = None
elif named:
declId = self._parse_nested_name()
else:
declId = None
-
- suffixOpts = []
+ arrayOps = []
while 1:
self.skip_ws()
if typed and self.skip_string('['):
- startPos = self.pos - 1
- openCount = 1
- while not self.eof:
- c = self.current_char
- 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.pos += 1
- suffixOpts.append(ASTArray(
- self.definition[startPos + 1:self.pos - 1].strip()))
+ value = self._parse_expression(end=[']'])
+ res = self.skip_string(']')
+ assert res
+ arrayOps.append(ASTArray(value))
continue
- if paramMode:
- paramQual = self._parse_parameters_and_qualifiers(paramMode)
- if paramQual:
- suffixOpts.append(paramQual)
- break
+ else:
+ break
+ paramQual = self._parse_parameters_and_qualifiers(paramMode)
+ return ASTDecleratorNameParamQual(declId=declId, arrayOps=arrayOps,
+ paramQual=paramQual)
- return ASTDeclerator(ptrOps, declId, suffixOpts)
+ def _parse_declerator(self, named, paramMode, typed=True):
+ # 'typed' here means 'parse return type stuff'
+ if paramMode not in ('type', 'function', 'operatorCast'):
+ raise Exception(
+ "Internal error, unknown paramMode '%s'." % paramMode)
+ prevErrors = []
+ self.skip_ws()
+ if typed and self.skip_string('*'):
+ self.skip_ws()
+ volatile = False
+ const = False
+ while 1:
+ if not volatile:
+ volatile = self.skip_word_and_ws('volatile')
+ if volatile:
+ continue
+ if not const:
+ const = self.skip_word_and_ws('const')
+ if const:
+ continue
+ break
+ next = self._parse_declerator(named, paramMode, typed)
+ return ASTDeclaratorPtr(next=next, volatile=volatile, const=const)
+ # TODO: shouldn't we parse an R-value ref here first?
+ if typed and self.skip_string("&"):
+ next = self._parse_declerator(named, paramMode, typed)
+ return ASTDeclaratorRef(next=next)
+ if typed and self.skip_string("..."):
+ next = self._parse_declerator(named, paramMode, False)
+ return ASTDeclaratorParamPack(next=next)
+ if typed: # pointer to member
+ pos = self.pos
+ try:
+ name = self._parse_nested_name(memberPointer=True)
+ self.skip_ws()
+ if not self.skip_string('*'):
+ self.fail("Expected '*' in pointer to member declarator.")
+ self.skip_ws()
+ except DefinitionError as e:
+ self.pos = pos
+ prevErrors.append((e, "If pointer to member declarator"))
+ else:
+ volatile = False
+ const = False
+ while 1:
+ if not volatile:
+ volatile = self.skip_word_and_ws('volatile')
+ if volatile:
+ continue
+ if not const:
+ const = self.skip_word_and_ws('const')
+ if const:
+ continue
+ break
+ next = self._parse_declerator(named, paramMode, typed)
+ return ASTDeclaratorMemPtr(name, const, volatile, next=next)
+ if typed and self.current_char == '(': # note: peeking, not skipping
+ if paramMode == "operatorCast":
+ # TODO: we should be able to parse cast operators which return
+ # function pointers. For now, just hax it and ignore.
+ return ASTDecleratorNameParamQual(declId=None, arrayOps=[],
+ paramQual=None)
+ # maybe this is the beginning of params and quals,try that first,
+ # otherwise assume it's noptr->declarator > ( ptr-declarator )
+ pos = self.pos
+ try:
+ # assume this is params and quals
+ res = self._parse_declarator_name_param_qual(named, paramMode,
+ typed)
+ return res
+ except DefinitionError as exParamQual:
+ prevErrors.append((exParamQual, "If declId, parameters, and qualifiers"))
+ self.pos = pos
+ try:
+ assert self.current_char == '('
+ self.skip_string('(')
+ # TODO: hmm, if there is a name, it must be in inner, right?
+ # TODO: hmm, if there must be parameters, they must b
+ # inside, right?
+ inner = self._parse_declerator(named, paramMode, typed)
+ if not self.skip_string(')'):
+ self.fail("Expected ')' in \"( ptr-declarator )\"")
+ next = self._parse_declerator(named=False,
+ paramMode="type",
+ typed=typed)
+ return ASTDeclaratorParen(inner=inner, next=next)
+ except DefinitionError as exNoPtrParen:
+ self.pos = pos
+ prevErrors.append((exNoPtrParen, "If parenthesis in noptr-declarator"))
+ header = "Error in declarator"
+ raise self._make_multi_error(prevErrors, header)
+ pos = self.pos
+ try:
+ return self._parse_declarator_name_param_qual(named, paramMode, typed)
+ except DefinitionError as e:
+ self.pos = pos
+ prevErrors.append((e, "If declarator-id"))
+ header = "Error in declarator or parameters and qualifiers"
+ raise self._make_multi_error(prevErrors, header)
def _parse_initializer(self, outer=None):
self.skip_ws()
@@ -1920,50 +3244,34 @@ class DefinitionParser(object):
else:
if outer == 'member':
value = self.read_rest().strip()
- return ASTInitializer(value)
+ elif outer == 'templateParam':
+ value = self._parse_expression(end=[',', '>'])
elif outer is None: # function parameter
- symbols = []
- startPos = self.pos
- self.skip_ws()
- if self.match(_string_re):
- 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
- self.pos += 1
- if self.eof:
- self.pos = startPos
- self.fail(
- 'Could not find end of default value for function '
- 'parameter.')
- value = self.definition[startPos:self.pos].strip()
- return ASTInitializer(value)
+ value = self._parse_expression(end=[',', ')'])
else:
- self.fail(
- "Internal error, initializer for outer '%s' not "
- "implemented." % outer)
+ self.fail("Internal error, initializer for outer '%s' not "
+ "implemented." % outer)
+ return ASTInitializer(value)
- def _parse_type(self, outer=None, named=False, allowParams=False):
+ def _parse_type(self, named, outer=None):
"""
named=False|'maybe'|True: 'maybe' is e.g., for function objects which
doesn't need to name the arguments
+
+ outer == operatorCast: annoying case, we should not take the params
"""
if outer: # always named
- if outer not in ('type', 'member', 'function'):
+ if outer not in ('type', 'member', 'function',
+ 'operatorCast', 'templateParam'):
raise Exception('Internal error, unknown outer "%s".' % outer)
- assert not named
+ if outer != 'operatorCast':
+ assert named
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
+ prevErrors = []
startPos = self.pos
# first try without the type
try:
@@ -1972,79 +3280,116 @@ class DefinitionParser(object):
typed=False)
self.assert_end()
except DefinitionError as exUntyped:
+ if outer == 'type':
+ desc = "If just a name"
+ elif outer == 'function':
+ desc = "If the function has no return type"
+ else:
+ assert False
+ prevErrors.append((exUntyped, desc))
self.pos = startPos
try:
declSpecs = self._parse_decl_specs(outer=outer)
decl = self._parse_declerator(named=True, paramMode=outer)
except DefinitionError as exTyped:
+ self.pos = startPos
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))
+ desc = "If typedef-like declaration"
+ elif outer == 'function':
+ desc = "If the function has a return type"
+ else:
+ assert False
+ prevErrors.append((exTyped, desc))
+ # Retain the else branch for easier debugging.
+ # TODO: it would be nice to save the previous stacktrace
+ # and output it here.
+ if True:
+ if outer == 'type':
+ header = "Type must be either just a name or a "
+ header += "typedef-like declaration."
+ elif outer == 'function':
+ header = "Error when parsing function declaration."
+ else:
+ assert False
+ raise self._make_multi_error(prevErrors, header)
else:
+ # For testing purposes.
# do it again to get the proper traceback (how do you
# relieable save a traceback when an exception is
# constructed?)
+ pass
self.pos = startPos
- declSpecs = self._parse_decl_specs(outer=outer)
- decl = self._parse_declerator(named=True,
- paramMode=outer)
+ typed = True
+ declSpecs = self._parse_decl_specs(outer=outer, typed=typed)
+ decl = self._parse_declerator(named=True, paramMode=outer,
+ typed=typed)
else:
- if outer:
+ paramMode = 'type'
+ if outer == 'member': # i.e., member
named = True
- allowParams = True
- if allowParams:
- paramMode = 'type'
- else:
- paramMode = None
+ elif outer == 'operatorCast':
+ paramMode = 'operatorCast'
+ outer = None
+ elif outer == 'templateParam':
+ named = 'single'
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):
+ def _parse_type_with_init(self, named, outer):
if outer:
- assert outer in ('type', 'member', 'function')
+ assert outer in ('type', 'member', 'function', 'templateParam')
type = self._parse_type(outer=outer, named=named)
init = self._parse_initializer(outer=outer)
return ASTTypeWithInit(type, init)
+ def _parse_type_using(self):
+ name = self._parse_nested_name()
+ self.skip_ws()
+ if not self.skip_string('='):
+ return ASTTypeUsing(name, None)
+ type = self._parse_type(False, None)
+ return ASTTypeUsing(name, type)
+
def _parse_class(self):
- classVisibility = 'public'
- if self.match(_visibility_re):
- classVisibility = self.matched_text
name = self._parse_nested_name()
+ self.skip_ws()
+ final = self.skip_word_and_ws('final')
bases = []
self.skip_ws()
if self.skip_string(':'):
while 1:
self.skip_ws()
visibility = 'private'
+ virtual = False
+ pack = False
+ if self.skip_word_and_ws('virtual'):
+ virtual = True
if self.match(_visibility_re):
visibility = self.matched_text
+ self.skip_ws()
+ if not virtual and self.skip_word_and_ws('virtual'):
+ virtual = True
baseName = self._parse_nested_name()
- bases.append(ASTBaseClass(baseName, visibility))
+ self.skip_ws()
+ pack = self.skip_string('...')
+ bases.append(ASTBaseClass(baseName, visibility, virtual, pack))
self.skip_ws()
if self.skip_string(','):
continue
else:
break
- return ASTClass(name, classVisibility, bases)
+ return ASTClass(name, final, bases)
def _parse_enum(self):
- scoped = None # is set by CPPEnumObject
- self.skip_ws()
- visibility = 'public'
- if self.match(_visibility_re):
- visibility = self.matched_text
+ scoped = None # is set by CPPEnumObject
self.skip_ws()
name = self._parse_nested_name()
self.skip_ws()
underlyingType = None
if self.skip_string(':'):
- underlyingType = self._parse_type()
- return ASTEnum(name, visibility, scoped, underlyingType)
+ underlyingType = self._parse_type(named=False)
+ return ASTEnum(name, scoped, underlyingType)
def _parse_enumerator(self):
name = self._parse_nested_name()
@@ -2055,47 +3400,195 @@ class DefinitionParser(object):
init = ASTInitializer(self.read_rest())
return ASTEnumerator(name, init)
- def parse_type_object(self):
- res = self._parse_type(outer='type')
- res.objectType = 'type'
- return res
+ def _parse_template_parameter_list(self):
+ # only: '<' parameter-list '>'
+ # we assume that 'template' has just been parsed
+ templateParams = []
+ self.skip_ws()
+ if not self.skip_string("<"):
+ self.fail("Expected '<' after 'template'")
+ while 1:
+ prevErrors = []
+ self.skip_ws()
+ if self.skip_word('template'):
+ # declare a tenplate template parameter
+ nestedParams = self._parse_template_parameter_list()
+ else:
+ nestedParams = None
+ self.skip_ws()
+ key = None
+ if self.skip_word_and_ws('typename'):
+ key = 'typename'
+ elif self.skip_word_and_ws('class'):
+ key = 'class'
+ elif nestedParams:
+ self.fail("Expected 'typename' or 'class' after "
+ "template template parameter list.")
+ if key:
+ # declare a type or template type parameter
+ self.skip_ws()
+ parameterPack = self.skip_string('...')
+ self.skip_ws()
+ if self.match(_identifier_re):
+ identifier = ASTIdentifier(self.matched_text)
+ else:
+ identifier = None
+ self.skip_ws()
+ if not parameterPack and self.skip_string('='):
+ default = self._parse_type(named=False, outer=None)
+ else:
+ default = None
+ data = ASTTemplateKeyParamPackIdDefault(key, identifier,
+ parameterPack, default)
+ if nestedParams:
+ # template type
+ param = ASTTemplateParamTemplateType(nestedParams, data)
+ else:
+ # type
+ param = ASTTemplateParamType(data)
+ templateParams.append(param)
+ else:
+ # declare a non-type parameter
+ pos = self.pos
+ try:
+ param = self._parse_type_with_init('maybe', 'templateParam')
+ templateParams.append(ASTTemplateParamNonType(param))
+ except DefinitionError as e:
+ prevErrors.append((e, "If non-type template parameter"))
+ self.pos = pos
+ self.skip_ws()
+ if self.skip_string('>'):
+ return ASTTemplateParams(templateParams)
+ elif self.skip_string(','):
+ continue
+ else:
+ header = "Error in template parameter list."
+ try:
+ self.fail('Expected "=", ",", or ">".')
+ except DefinitionError as e:
+ prevErrors.append((e, ""))
+ raise self._make_multi_error(prevErrors, header)
- def parse_member_object(self):
- res = self._parse_type_with_init(outer='member')
- res.objectType = 'member'
- return res
+ def _parse_template_declaration_prefix(self):
+ templates = []
+ while 1:
+ self.skip_ws()
+ if not self.skip_word("template"):
+ break
+ params = self._parse_template_parameter_list()
+ templates.append(params)
+ if len(templates) == 0:
+ return None
+ else:
+ return ASTTemplateDeclarationPrefix(templates)
- def parse_function_object(self):
- res = self._parse_type(outer='function')
- res.objectType = 'function'
- return res
+ def _check_template_consistency(self, nestedName, templatePrefix,
+ fullSpecShorthand):
+ numArgs = nestedName.num_templates()
+ if not templatePrefix:
+ numParams = 0
+ else:
+ numParams = len(templatePrefix.templates)
+ if numArgs + 1 < numParams:
+ self.fail("Too few template argument lists comapred to parameter"
+ " lists. Argument lists: %d, Parameter lists: %d."
+ % (numArgs, numParams))
+ if numArgs > numParams:
+ numExtra = numArgs - numParams
+ if not fullSpecShorthand:
+ msg = "Too many template argument lists compared to parameter" \
+ " lists. Argument lists: %d, Parameter lists: %d," \
+ " Extra empty parameters lists prepended: %d." \
+ % (numArgs, numParams, numExtra)
+ msg += " Declaration:\n\t"
+ if templatePrefix:
+ msg += "%s\n\t" % text_type(templatePrefix)
+ msg += text_type(nestedName)
+ self.warn(msg)
+
+ newTemplates = []
+ for i in range(numExtra):
+ newTemplates.append(ASTTemplateParams([]))
+ if templatePrefix:
+ newTemplates.extend(templatePrefix.templates)
+ templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
+ return templatePrefix
+
+ def parse_declaration(self, objectType):
+ if objectType not in ('type', 'member',
+ 'function', 'class', 'enum', 'enumerator'):
+ raise Exception('Internal error, unknown objectType "%s".' % objectType)
+ visibility = None
+ templatePrefix = None
+ declaration = None
- def parse_class_object(self):
- res = self._parse_class()
- res.objectType = 'class'
- return res
+ self.skip_ws()
+ if self.match(_visibility_re):
+ visibility = self.matched_text
- def parse_enum_object(self):
- res = self._parse_enum()
- res.objectType = 'enum'
- return res
+ if objectType in ('type', 'member', 'function', 'class'):
+ templatePrefix = self._parse_template_declaration_prefix()
- def parse_enumerator_object(self):
- res = self._parse_enumerator()
- res.objectType = 'enumerator'
- return res
+ if objectType == 'type':
+ prevErrors = []
+ pos = self.pos
+ try:
+ if not templatePrefix:
+ declaration = self._parse_type(named=True, outer='type')
+ except DefinitionError as e:
+ prevErrors.append((e, "If typedef-like declaration"))
+ self.pos = pos
+ pos = self.pos
+ try:
+ if not declaration:
+ declaration = self._parse_type_using()
+ except DefinitionError as e:
+ self.pos = pos
+ prevErrors.append((e, "If type alias or template alias"))
+ header = "Error in type declaration."
+ raise self._make_multi_error(prevErrors, header)
+ elif objectType == 'member':
+ declaration = self._parse_type_with_init(named=True, outer='member')
+ elif objectType == 'function':
+ declaration = self._parse_type(named=True, outer='function')
+ elif objectType == 'class':
+ declaration = self._parse_class()
+ elif objectType == 'enum':
+ declaration = self._parse_enum()
+ elif objectType == 'enumerator':
+ declaration = self._parse_enumerator()
+ else:
+ assert False
+ templatePrefix = self._check_template_consistency(declaration.name,
+ templatePrefix,
+ fullSpecShorthand=False)
+ return ASTDeclaration(objectType, visibility,
+ templatePrefix, declaration)
def parse_namespace_object(self):
- res = self._parse_nested_name()
+ templatePrefix = self._parse_template_declaration_prefix()
+ name = self._parse_nested_name()
+ templatePrefix = self._check_template_consistency(name, templatePrefix,
+ fullSpecShorthand=False)
+ res = ASTNamespace(name, templatePrefix)
res.objectType = 'namespace'
return res
def parse_xref_object(self):
- res = self._parse_nested_name()
+ templatePrefix = self._parse_template_declaration_prefix()
+ name = self._parse_nested_name()
+ templatePrefix = self._check_template_consistency(name, templatePrefix,
+ fullSpecShorthand=True)
+ res = ASTNamespace(name, templatePrefix)
res.objectType = 'xref'
return res
+def _make_phony_error_name():
+ nne = ASTNestedNameElement(ASTIdentifier("PhonyNameDueToError"), None)
+ return ASTNestedName([nne], rooted=False)
+
+
class CPPObject(ObjectDescription):
"""Description of a C++ language object."""
@@ -2103,6 +3596,9 @@ class CPPObject(ObjectDescription):
GroupedField('parameter', label=l_('Parameters'),
names=('param', 'parameter', 'arg', 'argument'),
can_collapse=True),
+ GroupedField('template parameter', label=l_('Template Parameters'),
+ names=('tparam', 'template parameter'),
+ can_collapse=True),
GroupedField('exceptions', label=l_('Throws'), rolename='cpp:class',
names=('throws', 'throw', 'exception'),
can_collapse=True),
@@ -2110,97 +3606,116 @@ class CPPObject(ObjectDescription):
names=('returns', 'return')),
]
+ def warn(self, msg):
+ self.state_machine.reporter.warning(msg, line=self.lineno)
+
+ def _add_enumerator_to_parent(self, ast):
+ assert ast.objectType == 'enumerator'
+ # find the parent, if it exists && is an enum
+ # && it's unscoped,
+ # then add the name to the parent scope
+ symbol = ast.symbol
+ assert symbol
+ assert symbol.identifier is not None
+ assert symbol.templateParams is None
+ assert symbol.templateArgs is None
+ parentSymbol = symbol.parent
+ assert parentSymbol
+ if parentSymbol.parent is None:
+ # TODO: we could warn, but it is somewhat equivalent to unscoped
+ # enums, without the enum
+ return # no parent
+ parentDecl = parentSymbol.declaration
+ if parentDecl is None:
+ # the parent is not explicitly declared
+ # TODO: we could warn, but it could be a style to just assume
+ # enumerator parnets to be scoped
+ return
+ if parentDecl.objectType != 'enum':
+ # TODO: maybe issue a warning, enumerators in non-enums is weird,
+ # but it is somewhat equivalent to unscoped enums, without the enum
+ return
+ if parentDecl.scoped:
+ return
+
+ targetSymbol = parentSymbol.parent
+ s = targetSymbol.find_identifier(symbol.identifier, matchSelf=False)
+ if s is not None:
+ # something is already declared with that name
+ return
+ declClone = symbol.declaration.clone()
+ declClone.enumeratorScopedSymbol = symbol
+ Symbol(parent=targetSymbol, identifier=symbol.identifier,
+ templateParams=None, templateArgs=None,
+ declaration=declClone,
+ docname=self.env.docname)
+
def add_target_and_index(self, ast, sig, signode):
- ids = [ # the newest should be first
- ast.get_id_v2(),
- ast.get_id_v1()
- ]
- theid = ids[0]
- ast.newestId = theid
- assert theid # shouldn't be None
- name = text_type(ast.prefixedName)
- if theid not in self.state.document.ids:
+ # general note: name must be lstrip(':')'ed, to remove "::"
+ try:
+ id_v1 = ast.get_id_v1()
+ except NoOldIdError:
+ id_v1 = None
+ id_v2 = ast.get_id_v2()
+ # store them in reverse order, so the newest is first
+ ids = [id_v2, id_v1]
+
+ newestId = ids[0]
+ assert newestId # shouldn't be None
+ if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId):
+ self.warn('Index id generation for C++ object "%s" failed, please '
+ 'report as bug (id=%s).' % (text_type(ast), newestId))
+
+ name = text_type(ast.symbol.get_full_nested_name()).lstrip(':')
+ indexText = self.get_index_text(name)
+ self.indexnode['entries'].append(('single', indexText, newestId, ''))
+
+ if newestId not in self.state.document.ids:
# if the name is not unique, the first one will win
- objects = self.env.domaindata['cpp']['objects']
- if name not in objects:
+ names = self.env.domaindata['cpp']['names']
+ if name not in names:
+ names[name] = ast.symbol.docname
signode['names'].append(name)
else:
+ # print("[CPP] non-unique name:", name)
pass
- #print("[CPP] non-unique name:", name)
for id in ids:
- if id: # is None when the element didn't exist in that version
+ if id: # is None when the element didn't exist in that version
signode['ids'].append(id)
- signode['first'] = (not self.names)
+ signode['first'] = (not self.names) # hmm, what is this abound?
self.state.document.note_explicit_target(signode)
- if not name in objects:
- objects.setdefault(name, (self.env.docname, ast))
- if ast.objectType == 'enumerator':
- # find the parent, if it exists && is an enum
- # && it's unscoped,
- # then add the name to the parent scope
- assert len(ast.prefixedName.names) > 0
- parentPrefixedAstName = ASTNestedName(ast.prefixedName.names[:-1])
- parentPrefixedName = text_type(parentPrefixedAstName)
- if parentPrefixedName in objects:
- docname, parentAst = objects[parentPrefixedName]
- if parentAst.objectType == 'enum' and not parentAst.scoped:
- enumeratorName = ASTNestedName([ast.prefixedName.names[-1]])
- assert len(parentAst.prefixedName.names) > 0
- enumScope = ASTNestedName(parentAst.prefixedName.names[:-1])
- unscopedName = enumeratorName.prefix_nested_name(enumScope)
- txtUnscopedName = text_type(unscopedName)
- if not txtUnscopedName in objects:
- objects.setdefault(txtUnscopedName,
- (self.env.docname, ast))
- # 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))
-
- 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.indexnode['entries'].append(('single', indextext, theid, ''))
def parse_definition(self, parser):
raise NotImplementedError()
- def describe_signature(self, signode, ast):
+ def describe_signature(self, signode, ast, parentScope):
raise NotImplementedError()
def handle_signature(self, sig, signode):
- def set_lastname(name):
- parent = self.env.ref_context.get('cpp:parent')
- if parent and len(parent) > 0:
- res = name.prefix_nested_name(parent[-1])
- else:
- res = name
- assert res
- self.env.ref_context['cpp:lastname'] = res
- return res
+ if 'cpp:parentSymbol' not in self.env.ref_context:
+ root = self.env.domaindata['cpp']['rootSymbol']
+ self.env.ref_context['cpp:parentSymbol'] = root
+ parentSymbol = self.env.ref_context['cpp:parentSymbol']
- parser = DefinitionParser(sig)
+ parser = DefinitionParser(sig, self)
try:
ast = self.parse_definition(parser)
parser.assert_end()
except DefinitionError as e:
- self.state_machine.reporter.warning(e.description,
- line=self.lineno)
+ self.warn(e.description)
# It is easier to assume some phony name than handling the error in
# the possibly inner declarations.
- name = ASTNestedName([
- ASTNestedNameElement("PhonyNameDueToError", None)
- ])
- set_lastname(name)
+ name = _make_phony_error_name()
+ symbol = parentSymbol.add_name(name)
+ self.env.ref_context['cpp:lastSymbol'] = symbol
raise ValueError
- self.describe_signature(signode, ast)
+ symbol = parentSymbol.add_declaration(ast, docname=self.env.docname)
+ self.env.ref_context['cpp:lastSymbol'] = symbol
+
+ if ast.objectType == 'enumerator':
+ self._add_enumerator_to_parent(ast)
- ast.prefixedName = set_lastname(ast.name)
- assert ast.prefixedName
+ self.describe_signature(signode, ast)
return ast
@@ -2209,10 +3724,9 @@ class CPPTypeObject(CPPObject):
return _('%s (C++ type)') % name
def parse_definition(self, parser):
- return parser.parse_type_object()
+ return parser.parse_declaration("type")
def describe_signature(self, signode, ast):
- signode += addnodes.desc_annotation('type ', 'type ')
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2221,7 +3735,7 @@ class CPPMemberObject(CPPObject):
return _('%s (C++ member)') % name
def parse_definition(self, parser):
- return parser.parse_member_object()
+ return parser.parse_declaration("member")
def describe_signature(self, signode, ast):
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2232,7 +3746,7 @@ class CPPFunctionObject(CPPObject):
return _('%s (C++ function)') % name
def parse_definition(self, parser):
- return parser.parse_function_object()
+ return parser.parse_declaration("function")
def describe_signature(self, signode, ast):
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2243,21 +3757,18 @@ class CPPClassObject(CPPObject):
return _('%s (C++ class)') % name
def before_content(self):
- lastname = self.env.ref_context['cpp:lastname']
- assert lastname
- if 'cpp:parent' in self.env.ref_context:
- self.env.ref_context['cpp:parent'].append(lastname)
- else:
- self.env.ref_context['cpp:parent'] = [lastname]
+ lastSymbol = self.env.ref_context['cpp:lastSymbol']
+ assert lastSymbol
+ self.oldParentSymbol = self.env.ref_context['cpp:parentSymbol']
+ self.env.ref_context['cpp:parentSymbol'] = lastSymbol
def after_content(self):
- self.env.ref_context['cpp:parent'].pop()
+ self.env.ref_context['cpp:parentSymbol'] = self.oldParentSymbol
def parse_definition(self, parser):
- return parser.parse_class_object()
+ return parser.parse_declaration("class")
def describe_signature(self, signode, ast):
- signode += addnodes.desc_annotation('class ', 'class ')
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2266,18 +3777,16 @@ class CPPEnumObject(CPPObject):
return _('%s (C++ enum)') % name
def before_content(self):
- lastname = self.env.ref_context['cpp:lastname']
- assert lastname
- if 'cpp:parent' in self.env.ref_context:
- self.env.ref_context['cpp:parent'].append(lastname)
- else:
- self.env.ref_context['cpp:parent'] = [lastname]
+ lastSymbol = self.env.ref_context['cpp:lastSymbol']
+ assert lastSymbol
+ self.oldParentSymbol = self.env.ref_context['cpp:parentSymbol']
+ self.env.ref_context['cpp:parentSymbol'] = lastSymbol
def after_content(self):
- self.env.ref_context['cpp:parent'].pop()
+ self.env.ref_context['cpp:parentSymbol'] = self.oldParentSymbol
def parse_definition(self, parser):
- ast = parser.parse_enum_object()
+ ast = parser.parse_declaration("enum")
# self.objtype is set by ObjectDescription in run()
if self.objtype == "enum":
ast.scoped = None
@@ -2290,11 +3799,6 @@ class CPPEnumObject(CPPObject):
return ast
def describe_signature(self, signode, ast):
- prefix = 'enum '
- if ast.scoped:
- prefix += ast.scoped
- prefix += ' '
- signode += addnodes.desc_annotation(prefix, prefix)
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2303,10 +3807,9 @@ class CPPEnumeratorObject(CPPObject):
return _('%s (C++ enumerator)') % name
def parse_definition(self, parser):
- return parser.parse_enumerator_object()
+ return parser.parse_declaration("enumerator")
def describe_signature(self, signode, ast):
- signode += addnodes.desc_annotation('enumerator ', 'enumerator ')
ast.describe_signature(signode, 'lastIsName', self.env)
@@ -2322,28 +3825,97 @@ class CPPNamespaceObject(Directive):
final_argument_whitespace = True
option_spec = {}
+ def warn(self, msg):
+ self.state_machine.reporter.warning(msg, line=self.lineno)
+
def run(self):
env = self.state.document.settings.env
+ rootSymbol = env.domaindata['cpp']['rootSymbol']
if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
- env.ref_context['cpp:parent'] = []
+ symbol = rootSymbol
+ stack = []
else:
- parser = DefinitionParser(self.arguments[0])
+ parser = DefinitionParser(self.arguments[0], self)
try:
- prefix = parser.parse_namespace_object()
+ ast = parser.parse_namespace_object()
parser.assert_end()
except DefinitionError as e:
- self.state_machine.reporter.warning(e.description,
- line=self.lineno)
- else:
- env.ref_context['cpp:parent'] = [prefix]
+ self.warn(e.description)
+ name = _make_phony_error_name()
+ ast = ASTNamespace(name, None)
+ symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix)
+ stack = [symbol]
+ env.ref_context['cpp:parentSymbol'] = symbol
+ env.temp_data['cpp:namespaceStack'] = stack
+ return []
+
+
+class CPPNamespacePushObject(Directive):
+ has_content = False
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+ option_spec = {}
+
+ def warn(self, msg):
+ self.state_machine.reporter.warning(msg, line=self.lineno)
+
+ def run(self):
+ env = self.state.document.settings.env
+ if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
+ return
+ parser = DefinitionParser(self.arguments[0], self)
+ try:
+ ast = parser.parse_namespace_object()
+ parser.assert_end()
+ except DefinitionError as e:
+ self.warn(e.description)
+ name = _make_phony_error_name()
+ ast = ASTNamespace(name, None)
+ oldParent = env.ref_context.get('cpp:parentSymbol', None)
+ if not oldParent:
+ oldParent = env.domaindata['cpp']['rootSymbol']
+ symbol = oldParent.add_name(ast.nestedName, ast.templatePrefix)
+ stack = env.temp_data.get('cpp:namespaceStack', [])
+ stack.append(symbol)
+ env.ref_context['cpp:parentSymbol'] = symbol
+ env.temp_data['cpp:namespaceStack'] = stack
+ return []
+
+
+class CPPNamespacePopObject(Directive):
+ has_content = False
+ required_arguments = 0
+ optional_arguments = 0
+ final_argument_whitespace = True
+ option_spec = {}
+
+ def warn(self, msg):
+ self.state_machine.reporter.warning(msg, line=self.lineno)
+
+ def run(self):
+ env = self.state.document.settings.env
+ stack = env.temp_data.get('cpp:namespaceStack', None)
+ if not stack or len(stack) == 0:
+ self.warn("C++ namespace pop on empty stack. Defaulting to gobal scope.")
+ stack = []
+ else:
+ stack.pop()
+ if len(stack) > 0:
+ symbol = stack[-1]
+ else:
+ symbol = env.domaindata['cpp']['rootSymbol']
+ env.ref_context['cpp:parentSymbol'] = symbol
+ env.temp_data['cpp:namespaceStack'] = stack
return []
class CPPXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
- parent = env.ref_context.get('cpp:parent')
+ parent = env.ref_context.get('cpp:parentSymbol', None)
if parent:
- refnode['cpp:parent'] = parent[:]
+ refnode['cpp:parentKey'] = parent.get_lookup_key()
+ # TODO: should this really be here?
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
@@ -2355,6 +3927,7 @@ class CPPXRefRole(XRefRole):
title = title[dcolon + 2:]
return title, target
+
class CPPDomain(Domain):
"""C++ language domain."""
name = 'cpp'
@@ -2378,7 +3951,9 @@ class CPPDomain(Domain):
'enum-struct': CPPEnumObject,
'enum-class': CPPEnumObject,
'enumerator': CPPEnumeratorObject,
- 'namespace': CPPNamespaceObject
+ 'namespace': CPPNamespaceObject,
+ 'namespace-push': CPPNamespacePushObject,
+ 'namespace-pop': CPPNamespacePopObject
}
roles = {
'any': CPPXRefRole(),
@@ -2391,69 +3966,105 @@ class CPPDomain(Domain):
'enumerator': CPPXRefRole()
}
initial_data = {
- 'objects': {}, # prefixedName -> (docname, ast)
+ 'rootSymbol': Symbol(None, None, None, None, None, None),
+ 'names': {} # full name for indexing -> docname
}
def clear_doc(self, docname):
- for fullname, data in list(self.data['objects'].items()):
- if data[0] == docname:
- del self.data['objects'][fullname]
+ rootSymbol = self.data['rootSymbol']
+ rootSymbol.clear_doc(docname)
+ for name, nDocname in list(self.data['names'].items()):
+ if nDocname == docname:
+ del self.data['names'][name]
+
+ def process_doc(self, env, docname, document):
+ # just for debugging
+ # print(docname)
+ # print(self.data['rootSymbol'].dump(0))
+ pass
def merge_domaindata(self, docnames, otherdata):
- # XXX check duplicates
- for fullname, data in otherdata['objects'].items():
- if data[0] in docnames:
- self.data['objects'][fullname] = data
+ self.data['rootSymbol'].merge_with(otherdata['rootSymbol'],
+ docnames, self.env)
+ ourNames = self.data['names']
+ for name, docname in otherdata['names'].items():
+ if docname in docnames:
+ if name in ourNames:
+ msg = "Duplicate declaration, also defined in '%s'.\n"
+ msg += "Name of declaration is '%s'."
+ msg = msg % (ourNames[name], name)
+ self.env.warn(docname, msg)
+ else:
+ ourNames[name] = docname
def _resolve_xref_inner(self, env, fromdocname, builder,
- target, node, contnode, warn=True):
- def _create_refnode(nameAst):
- name = text_type(nameAst)
- if name not in self.data['objects']:
- # try dropping the last template
- name = nameAst.get_name_no_last_template()
- if name not in self.data['objects']:
- return None, None
- docname, ast = self.data['objects'][name]
- return make_refnode(builder, fromdocname, docname, ast.newestId,
- contnode, name), ast.objectType
-
- parser = DefinitionParser(target)
+ target, node, contnode, emitWarnings=True):
+ class Warner(object):
+ def warn(self, msg):
+ if emitWarnings:
+ env.warn_node(msg, node)
+ warner = Warner()
+ parser = DefinitionParser(target, warner)
try:
- nameAst = parser.parse_xref_object().name
+ ast = parser.parse_xref_object()
parser.skip_ws()
- if not parser.eof:
- raise DefinitionError('')
- except DefinitionError:
- if warn:
- env.warn_node('unparseable C++ definition: %r' % target, node)
+ parser.assert_end()
+ except DefinitionError as e:
+ warner.warn('Unparseable C++ cross-reference: %r\n%s'
+ % (target, str(e.description)))
return None, None
+ parentKey = node.get("cpp:parentKey", None)
+ rootSymbol = self.data['rootSymbol']
+ if parentKey:
+ parentSymbol = rootSymbol.direct_lookup(parentKey)
+ if not parentSymbol:
+ print("Target: ", target)
+ print("ParentKey: ", parentKey)
+ assert parentSymbol # should be there
+ else:
+ parentSymbol = rootSymbol
- # try as is the name is fully qualified
- res = _create_refnode(nameAst)
- if res[0]:
- return res
-
- # 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]))
+ name = ast.nestedName
+ if ast.templatePrefix:
+ templateDecls = ast.templatePrefix.templates
else:
+ templateDecls = []
+ s = parentSymbol.find_name(name, templateDecls,
+ templateShorthand=True,
+ matchSelf=True)
+ if s is None or s.declaration is None:
return None, None
+ declaration = s.declaration
+ fullNestedName = s.get_full_nested_name()
+ name = text_type(fullNestedName).lstrip(':')
+ docname = s.docname
+ assert docname
+ return make_refnode(builder, fromdocname, docname,
+ declaration.get_newest_id(), contnode, name
+ ), declaration.objectType
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
- return self._resolve_xref_inner(env, fromdocname, builder, target, node,
- contnode)[0]
+ return self._resolve_xref_inner(env, fromdocname, builder, target,
+ node, contnode)[0]
def resolve_any_xref(self, env, fromdocname, builder, target,
node, contnode):
node, objtype = self._resolve_xref_inner(env, fromdocname, builder,
- target, node, contnode, warn=False)
+ target, node, contnode,
+ emitWarnings=False)
if node:
return [('cpp:' + self.role_for_objtype(objtype), node)]
return []
def get_objects(self):
- for refname, (docname, ast) in iteritems(self.data['objects']):
- yield (refname, refname, ast.objectType, docname, ast.newestId, 1)
+ rootSymbol = self.data['rootSymbol']
+ for symbol in rootSymbol.get_all_symbols():
+ if symbol.declaration is None:
+ continue
+ assert symbol.docname
+ name = text_type(symbol.get_full_nested_name()).lstrip(':')
+ objectType = symbol.declaration.objectType
+ docname = symbol.docname
+ newestId = symbol.declaration.get_newest_id()
+ yield (name, name, objectType, docname, newestId, 1)