C++, fixes to id generation.

New ids are prefixed with "_CPPv1". Replacing the prefix with "_Z"
should yield a valid mangled name following the Itanium C++ ABI,
except for expressions, which are currently not handled.
This commit is contained in:
Jakob Lykke Andersen 2015-02-06 08:35:12 +01:00
parent f4cf30b95e
commit fb3a524a90
2 changed files with 46 additions and 12 deletions

View File

@ -189,7 +189,6 @@ _operator_re = re.compile(r'''(?x)
# Id v1 constants
#-------------------------------------------------------------------------------
_id_prefix_v1 = '_CPP'
_id_fundamental_v1 = {
'char': 'c',
'signed char': 'c',
@ -264,7 +263,7 @@ _id_operator_v1 = {
# Id v2 constants
#-------------------------------------------------------------------------------
_id_prefix_v2 = '_CPP'
_id_prefix_v2 = '_CPPv2'
_id_fundamental_v2 = {
# not all of these are actually parsed as fundamental types, TODO: do that
'void': 'v',
@ -517,6 +516,9 @@ class ASTNestedNameElement(ASTBase):
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)
@ -591,13 +593,14 @@ class ASTNestedName(ASTBase):
res.append(n.get_id_v1())
return u'::'.join(res)
def get_id_v2(self):
def get_id_v2(self, modifiers=""):
res = []
if len(self.names) > 1:
if len(self.names) > 1 or len(modifiers) > 0:
res.append('N')
res.append(modifiers)
for n in self.names:
res.append(n.get_id_v2())
if len(self.names) > 1:
if len(self.names) > 1 or len(modifiers) > 0:
res.append('E')
return u''.join(res)
@ -867,6 +870,17 @@ class ASTDeclSpecsSimple(ASTBase):
self.volatile = volatile
self.const = const
def mergeWith(self, other):
if not other:
return self
return ASTDeclSpecsSimple(self.storage or other.storage,
self.inline or other.inline,
self.virtual or other.virtual,
self.explicit or other.explicit,
self.constexpr or other.constexpr,
self.volatile or other.volatile,
self.const or other.const)
def __unicode__(self):
res = []
if self.storage:
@ -905,12 +919,16 @@ class ASTDeclSpecsSimple(ASTBase):
if self.const:
_add(modifiers, 'const')
class ASTDeclSpecs(ASTBase):
def __init__(self, outer, visibility, 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)
self.trailingTypeSpec = trailing
@property
@ -920,9 +938,9 @@ class ASTDeclSpecs(ASTBase):
def get_id_v1(self):
res = []
res.append(self.trailingTypeSpec.get_id_v1())
if self.leftSpecs.volatile or self.rightSpecs.volatile:
if self.allSpecs.volatile:
res.append('V')
if self.leftSpecs.const or self.rightSpecs.const:
if self.allSpecs.const:
res.append('C')
return u''.join(res)
@ -986,6 +1004,7 @@ class ASTDeclSpecs(ASTBase):
for m in modifiers:
signode += m
class ASTPtrOpPtr(ASTBase):
def __init__(self, volatile, const):
self.volatile = volatile
@ -1194,8 +1213,8 @@ class ASTType(ASTBase):
if self.objectType: # needs the name
res.append(_id_prefix_v2)
if self.objectType == 'function': # also modifiers
res.append(self.decl.get_modifiers_id_v2())
res.append(self.prefixedName.get_id_v2())
modifiers = self.decl.get_modifiers_id_v2()
res.append(self.prefixedName.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())
@ -1225,6 +1244,7 @@ class ASTType(ASTBase):
signode += nodes.Text(' ')
self.decl.describe_signature(signode, mode, env)
class ASTTypeWithInit(ASTBase):
def __init__(self, type, init):
self.objectType = None
@ -1939,7 +1959,7 @@ class CPPObject(ObjectDescription):
ast.get_id_v2(),
ast.get_id_v1()
]
theid = ids[1] # TODO: change to [0] before final version
theid = ids[0]
name = text_type(ast.prefixedName)
if theid not in self.state.document.ids:
# if the name is not unique, the first one will win

View File

@ -15,6 +15,8 @@ from util import raises
from sphinx.domains.cpp import DefinitionParser, DefinitionError
ids = []
def parse(name, string):
parser = DefinitionParser(string)
res = getattr(parser, "parse_" + name + "_object")()
@ -37,10 +39,12 @@ def check(name, input, output=None):
print("Expected: ", output)
raise DefinitionError("")
ast.describe_signature([], 'lastIsName', None)
ast.prefixedName = ast.name # otherwise the get_id fails, it would be set
# in handle_signarue
# Artificially set the prefixedName, otherwise the get_id fails.
# It would usually have been set in handle_signarue.
ast.prefixedName = ast.name
ast.get_id_v1()
ast.get_id_v2()
ids.append(ast.get_id_v2())
#print ".. %s:: %s" % (name, input)
def test_type_definitions():
@ -79,6 +83,9 @@ def test_type_definitions():
check('member', 'module::myclass foo[n]')
check('function', 'operator bool() const')
check('function', 'A::operator bool() const')
check('function', 'A::operator bool() volatile const &')
check('function', 'A::operator bool() volatile const &&')
check('function', 'bool namespaced::theclass::method(arg1, arg2)')
x = 'std::vector<std::pair<std::string, int>> &module::test(register ' \
'foo, bar, std::string baz = "foobar, blah, bleh") const = 0'
@ -99,6 +106,7 @@ def test_type_definitions():
check('function', 'static constexpr int get_value()')
check('function', 'int get_value() const noexcept')
check('function', 'int get_value() const noexcept = delete')
check('function', 'int get_value() volatile const')
check('function', 'MyClass::MyClass(MyClass::MyClass&&) = default')
check('function', 'virtual MyClass::a_virtual_function() const override')
check('function', 'A B() override')
@ -142,3 +150,9 @@ def test_operators():
check('function', 'void operator bool() const', 'void operator bool() const')
for op in '*-+=/%!':
check('function', 'void operator %s ()' % op, 'void operator%s()' % op)
#def test_print():
# # used for getting all the ids out for checking
# for a in ids:
# print(a)
# raise DefinitionError("")