From fb3a524a903038da0ac4fc3be4aa657cc27a1fc8 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Fri, 6 Feb 2015 08:35:12 +0100 Subject: [PATCH] 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. --- sphinx/domains/cpp.py | 40 ++++++++++++++++++++++++++++++---------- tests/test_domain_cpp.py | 18 ++++++++++++++++-- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 95f7bcc75..9a20519fa 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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 diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 3b9d9420d..e690394da 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -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> &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("") \ No newline at end of file