# Buggy code
def _resolve_xref_inner(
    self, env, fromdocname, builder, typ, target, node, contnode, emitWarnings=True
):
    # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node, bool) -> nodes.Node  # NOQA

    class Warner(object):
        def warn(self, msg):
            if emitWarnings:
                logger.warning(msg, location=node)

    warner = Warner()
    # add parens again for those that could be functions
    if typ == "any" or typ == "func":
        target += "()"
    parser = DefinitionParser(target, warner, env.config)
    try:
        ast, isShorthand = parser.parse_xref_object()
        parser.assert_end()
    except DefinitionError as e:

        def findWarning(e):  # as arg to stop flake8 from complaining
            if typ != "any" and typ != "func":
                return target, e
            # hax on top of the paren hax to try to get correct errors
            parser2 = DefinitionParser(target[:-2], warner, env.config)
            try:
                parser2.parse_xref_object()
                parser2.assert_end()
            except DefinitionError as e2:
                return target[:-2], e2
            # strange, that we don't get the error now, use the original
            return target, e

        t, ex = findWarning(e)
        warner.warn(
            "Unparseable C++ cross-reference: %r\n%s" % (t, text_type(ex.description))
        )
        return None, None
    parentKey = node.get("cpp:parent_key", None)
    rootSymbol = self.data["root_symbol"]
    if parentKey:
        parentSymbol = rootSymbol.direct_lookup(parentKey)
        if not parentSymbol:
            print("Target: ", target)
            print("ParentKey: ", parentKey)
            print(rootSymbol.dump(1))
        assert parentSymbol  # should be there
    else:
        parentSymbol = rootSymbol

    if isShorthand:
        ns = ast  # type: ASTNamespace
        name = ns.nestedName
        if ns.templatePrefix:
            templateDecls = ns.templatePrefix.templates
        else:
            templateDecls = []
        s = parentSymbol.find_name(
            name,
            templateDecls,
            typ,
            templateShorthand=True,
            matchSelf=True,
            recurseInAnon=True,
        )
    else:
        decl = ast  # type: ASTDeclaration
        s = parentSymbol.find_declaration(
            decl, typ, templateShorthand=True, matchSelf=True, recurseInAnon=True
        )
    if s is None or s.declaration is None:
        txtName = text_type(name)
        if txtName.startswith("std::") or txtName == "std":
            raise NoUri()
        return None, None

    if typ.startswith("cpp:"):
        typ = typ[4:]
    if typ == "func":
        typ = "function"
    declTyp = s.declaration.objectType

    def checkType():
        if typ == "any" or typ == "identifier":
            return True
        if declTyp == "templateParam":
            return True
        objtypes = self.objtypes_for_role(typ)
        if objtypes:
            return declTyp in objtypes
        print("Type is %s, declType is %s" % (typ, declTyp))
        assert False

    if not checkType():
        warner.warn(
            "cpp:%s targets a %s (%s)."
            % (typ, s.declaration.objectType, s.get_full_nested_name())
        )

    declaration = s.declaration
    if isShorthand:
        fullNestedName = s.get_full_nested_name()
        displayName = fullNestedName.get_display_string().lstrip(":")
    else:
        displayName = decl.get_display_string()
    docname = s.docname
    assert docname

    # the non-identifier refs are cross-references, which should be processed:
    # - fix parenthesis due to operator() and add_function_parentheses
    if typ != "identifier":
        title = contnode.pop(0).astext()
        # If it's operator(), we need to add '()' if explicit function parens
        # are requested. Then the Sphinx machinery will add another pair.
        # Also, if it's an 'any' ref that resolves to a function, we need to add
        # parens as well.
        # However, if it's a non-shorthand function ref, for a function that
        # takes no arguments, then we may need to add parens again as well.
        addParen = 0
        if not node.get("refexplicit", False) and declaration.objectType == "function":
            if isShorthand:
                # this is just the normal haxing for 'any' roles
                if env.config.add_function_parentheses and typ == "any":
                    addParen += 1
                # and now this stuff for operator()
                if (
                    env.config.add_function_parentheses
                    and typ == "function"
                    and title.endswith("operator()")
                ):
                    addParen += 1
                if (
                    (typ == "any" or typ == "function")
                    and title.endswith("operator")
                    and displayName.endswith("operator()")
                ):
                    addParen += 1
            else:
                # our job here is to essentially nullify add_function_parentheses
                if env.config.add_function_parentheses:
                    if typ == "any" and displayName.endswith("()"):
                        addParen += 1
                    elif typ == "function":
                        if title.endswith("()") and not displayName.endswith("()"):
                            title = title[:-2]
                else:
                    if displayName.endswith("()"):
                        addParen += 1
        if addParen > 0:
            title += "()" * addParen
        # and reconstruct the title again
        contnode += nodes.Text(title)
    return make_refnode(
        builder,
        fromdocname,
        docname,
        declaration.get_newest_id(),
        contnode,
        displayName,
    ), declaration.objectType


# Traceback (AttributeError)
# Sphinx version: 1.8.0b1
# Python version: 2.7.13 (CPython)
# Docutils version: 0.14
# Jinja2 version: 2.10
# Last messages:
#   writing output... [ 18%] api/classsimgrid_1_1s4u_1_1Actor
#   writing output... [ 27%] api/dir_include
#   writing output... [ 36%] api/file_include_the_code.hpp
#   writing output... [ 45%] api/file_view_hierarchy
#   writing output... [ 54%] api/library_root
#   writing output... [ 63%] api/namespace_simgrid
#   writing output... [ 72%] api/namespace_simgrid__s4u
#   writing output... [ 81%] api/program_listing_file_include_the_code.hpp
#   writing output... [ 90%] api/unabridged_api
#   writing output... [100%] index
# Loaded extensions:
#   sphinx.ext.mathjax (1.8.0b1) from /home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/ext/mathjax.pyc
#   alabaster (0.7.11) from /home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/alabaster/__init__.pyc
#   breathe (4.10.0) from /home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/breathe/__init__.pyc
#   exhale (0.2.0) from /home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/exhale/__init__.pyc
Traceback (most recent call last):
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/cmd/build.py", line 304, in build_main
app.build(args.force_all, filenames)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/application.py", line 341, in build
self.builder.build_update()
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 350, in build_update
len(to_build))
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 415, in build
self.write(docnames, list(updated_docnames), method)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 596, in write
self._write_serial(sorted(docnames))
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 604, in _write_serial
doctree = self.env.get_and_resolve_doctree(docname, self)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/environment/__init__.py", line 623, in get_and_resolve_doctree
self.apply_post_transforms(doctree, docname)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/environment/__init__.py", line 670, in apply_post_transforms
transformer.apply_transforms()
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/transforms/__init__.py", line 90, in apply_transforms
Transformer.apply_transforms(self)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/docutils/transforms/__init__.py", line 171, in apply_transforms
transform.apply(**kwargs)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/transforms/post_transforms/__init__.py", line 90, in apply
typ, target, node, contnode)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/domains/cpp.py", line 6767, in resolve_xref
target, node, contnode)[0]
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/domains/cpp.py", line 6683, in _resolve_xref_inner
matchSelf=True, recurseInAnon=True)
File "/home/sven/Desktop/sphinx-cpp-cross-ref-bug/docs_exhale/venv/lib/python2.7/site-packages/sphinx/domains/cpp.py", line 4170, in find_declaration
candId = symbol.declaration.get_newest_id()
AttributeError: 'NoneType' object has no attribute 'get_newest_id'

# Fixed code
def _resolve_xref_inner(
    self, env, fromdocname, builder, typ, target, node, contnode, emitWarnings=True
):
    # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node, bool) -> nodes.Node  # NOQA

    class Warner(object):
        def warn(self, msg):
            if emitWarnings:
                logger.warning(msg, location=node)

    warner = Warner()
    # add parens again for those that could be functions
    if typ == "any" or typ == "func":
        target += "()"
    parser = DefinitionParser(target, warner, env.config)
    try:
        ast, isShorthand = parser.parse_xref_object()
        parser.assert_end()
    except DefinitionError as e:

        def findWarning(e):  # as arg to stop flake8 from complaining
            if typ != "any" and typ != "func":
                return target, e
            # hax on top of the paren hax to try to get correct errors
            parser2 = DefinitionParser(target[:-2], warner, env.config)
            try:
                parser2.parse_xref_object()
                parser2.assert_end()
            except DefinitionError as e2:
                return target[:-2], e2
            # strange, that we don't get the error now, use the original
            return target, e

        t, ex = findWarning(e)
        warner.warn(
            "Unparseable C++ cross-reference: %r\n%s" % (t, text_type(ex.description))
        )
        return None, None
    parentKey = node.get("cpp:parent_key", None)
    rootSymbol = self.data["root_symbol"]
    if parentKey:
        parentSymbol = rootSymbol.direct_lookup(parentKey)
        if not parentSymbol:
            print("Target: ", target)
            print("ParentKey: ", parentKey)
            print(rootSymbol.dump(1))
        assert parentSymbol  # should be there
    else:
        parentSymbol = rootSymbol

    if isShorthand:
        ns = ast  # type: ASTNamespace
        name = ns.nestedName
        if ns.templatePrefix:
            templateDecls = ns.templatePrefix.templates
        else:
            templateDecls = []
        s = parentSymbol.find_name(
            name,
            templateDecls,
            typ,
            templateShorthand=True,
            matchSelf=True,
            recurseInAnon=True,
        )
    else:
        decl = ast  # type: ASTDeclaration
        name = decl.name
        s = parentSymbol.find_declaration(
            decl, typ, templateShorthand=True, matchSelf=True, recurseInAnon=True
        )
    if s is None or s.declaration is None:
        txtName = text_type(name)
        if txtName.startswith("std::") or txtName == "std":
            raise NoUri()
        return None, None

    if typ.startswith("cpp:"):
        typ = typ[4:]
    if typ == "func":
        typ = "function"
    declTyp = s.declaration.objectType

    def checkType():
        if typ == "any" or typ == "identifier":
            return True
        if declTyp == "templateParam":
            return True
        objtypes = self.objtypes_for_role(typ)
        if objtypes:
            return declTyp in objtypes
        print("Type is %s, declType is %s" % (typ, declTyp))
        assert False

    if not checkType():
        warner.warn(
            "cpp:%s targets a %s (%s)."
            % (typ, s.declaration.objectType, s.get_full_nested_name())
        )

    declaration = s.declaration
    if isShorthand:
        fullNestedName = s.get_full_nested_name()
        displayName = fullNestedName.get_display_string().lstrip(":")
    else:
        displayName = decl.get_display_string()
    docname = s.docname
    assert docname

    # the non-identifier refs are cross-references, which should be processed:
    # - fix parenthesis due to operator() and add_function_parentheses
    if typ != "identifier":
        title = contnode.pop(0).astext()
        # If it's operator(), we need to add '()' if explicit function parens
        # are requested. Then the Sphinx machinery will add another pair.
        # Also, if it's an 'any' ref that resolves to a function, we need to add
        # parens as well.
        # However, if it's a non-shorthand function ref, for a function that
        # takes no arguments, then we may need to add parens again as well.
        addParen = 0
        if not node.get("refexplicit", False) and declaration.objectType == "function":
            if isShorthand:
                # this is just the normal haxing for 'any' roles
                if env.config.add_function_parentheses and typ == "any":
                    addParen += 1
                # and now this stuff for operator()
                if (
                    env.config.add_function_parentheses
                    and typ == "function"
                    and title.endswith("operator()")
                ):
                    addParen += 1
                if (
                    (typ == "any" or typ == "function")
                    and title.endswith("operator")
                    and displayName.endswith("operator()")
                ):
                    addParen += 1
            else:
                # our job here is to essentially nullify add_function_parentheses
                if env.config.add_function_parentheses:
                    if typ == "any" and displayName.endswith("()"):
                        addParen += 1
                    elif typ == "function":
                        if title.endswith("()") and not displayName.endswith("()"):
                            title = title[:-2]
                else:
                    if displayName.endswith("()"):
                        addParen += 1
        if addParen > 0:
            title += "()" * addParen
        # and reconstruct the title again
        contnode += nodes.Text(title)
    return make_refnode(
        builder,
        fromdocname,
        docname,
        declaration.get_newest_id(),
        contnode,
        displayName,
    ), declaration.objectType