# Buggy code
def _add_symbols(self, nestedName, templateDecls, declaration, docname):
    # type: (ASTNestedName, List[Any], ASTDeclaration, unicode) -> Symbol
    # Used for adding a whole path of symbols, where the last may or may not
    # be an actual declaration.

    def onMissingQualifiedSymbol(parentSymbol, identOrOp, templateParams, templateArgs):
        # type: (Symbol, Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs) -> Symbol
        return Symbol(
            parent=parentSymbol,
            identOrOp=identOrOp,
            templateParams=templateParams,
            templateArgs=templateArgs,
            declaration=None,
            docname=None,
        )

    lookupResult = self._symbol_lookup(
        nestedName,
        templateDecls,
        onMissingQualifiedSymbol,
        strictTemplateParamArgLists=True,
        ancestorLookupType=None,
        templateShorthand=False,
        matchSelf=False,
        recurseInAnon=True,
        correctPrimaryTemplateArgs=True,
    )
    assert (
        lookupResult is not None
    )  # we create symbols all the way, so that can't happen
    # TODO: actually do the iteration over results, though let's find a test case first
    try:
        symbol = next(lookupResult.symbols)
    except StopIteration:
        symbol = None

    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 function overload, so let's compare ids.
        isRedeclaration = True
        candSymbol = Symbol(
            parent=lookupResult.parentSymbol,
            identOrOp=lookupResult.identOrOp,
            templateParams=lookupResult.templateParams,
            templateArgs=lookupResult.templateArgs,
            declaration=declaration,
            docname=docname,
        )
        if declaration.objectType == "function":
            newId = declaration.get_newest_id()
            oldId = symbol.declaration.get_newest_id()
            if newId != oldId:
                # we already inserted the symbol, so return the new one
                symbol = candSymbol
                isRedeclaration = False
        if isRedeclaration:
            # Redeclaration of the same symbol.
            # Let the new one be there, but raise an error to the client
            # so it can use the real symbol as subscope.
            # This will probably result in a duplicate id warning.
            candSymbol.isRedeclaration = True
            raise _DuplicateSymbolError(symbol, declaration)
    else:
        symbol = Symbol(
            parent=lookupResult.parentSymbol,
            identOrOp=lookupResult.identOrOp,
            templateParams=lookupResult.templateParams,
            templateArgs=lookupResult.templateArgs,
            declaration=declaration,
            docname=docname,
        )
    return symbol


# 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 _add_symbols(self, nestedName, templateDecls, declaration, docname):
    # type: (ASTNestedName, List[Any], ASTDeclaration, unicode) -> Symbol
    # Used for adding a whole path of symbols, where the last may or may not
    # be an actual declaration.

    if Symbol.debug_lookup:
        print("_add_symbols:")
        print("   tdecls:", templateDecls)
        print("   nn:    ", nestedName)
        print("   decl:  ", declaration)
        print("   doc:   ", docname)

    def onMissingQualifiedSymbol(parentSymbol, identOrOp, templateParams, templateArgs):
        # type: (Symbol, Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs) -> Symbol
        if Symbol.debug_lookup:
            print("   _add_symbols, onMissingQualifiedSymbol:")
            print("      templateParams:", templateParams)
            print("      identOrOp:     ", identOrOp)
            print("      templateARgs:  ", templateArgs)
        return Symbol(
            parent=parentSymbol,
            identOrOp=identOrOp,
            templateParams=templateParams,
            templateArgs=templateArgs,
            declaration=None,
            docname=None,
        )

    lookupResult = self._symbol_lookup(
        nestedName,
        templateDecls,
        onMissingQualifiedSymbol,
        strictTemplateParamArgLists=True,
        ancestorLookupType=None,
        templateShorthand=False,
        matchSelf=False,
        recurseInAnon=True,
        correctPrimaryTemplateArgs=True,
    )
    assert (
        lookupResult is not None
    )  # we create symbols all the way, so that can't happen
    symbols = list(lookupResult.symbols)
    if len(symbols) == 0:
        if Symbol.debug_lookup:
            print("   _add_symbols, result, no symbol:")
            print("      templateParams:", lookupResult.templateParams)
            print("      identOrOp:     ", lookupResult.identOrOp)
            print("      templateArgs:  ", lookupResult.templateArgs)
            print("      declaration:   ", declaration)
            print("      docname:       ", docname)
        symbol = Symbol(
            parent=lookupResult.parentSymbol,
            identOrOp=lookupResult.identOrOp,
            templateParams=lookupResult.templateParams,
            templateArgs=lookupResult.templateArgs,
            declaration=declaration,
            docname=docname,
        )
        return symbol

    if Symbol.debug_lookup:
        print("   _add_symbols, result, symbols:")
        print("      number symbols:", len(symbols))

    if not declaration:
        if Symbol.debug_lookup:
            print("      no delcaration")
        # good, just a scope creation
        # TODO: what if we have more than one symbol?
        return symbols[0]

    noDecl = []
    withDecl = []
    for s in symbols:
        if s.declaration is None:
            noDecl.append(s)
        else:
            withDecl.append(s)
    if Symbol.debug_lookup:
        print("      #noDecl:  ", len(noDecl))
        print("      #withDecl:", len(withDecl))
    # assert len(noDecl) <= 1  # we should fill in symbols when they are there
    # TODO: enable assertion when we at some point find out how to do cleanup
    # With partial builds we may start with a large symbol tree stripped of declarations.

    # First check if one of those with a declaration matches.
    # If it's a function, we need to compare IDs,
    # otherwise there should be only one symbol with a declaration.
    def makeCandSymbol():
        if Symbol.debug_lookup:
            print("      begin: creating candidate symbol")
        symbol = Symbol(
            parent=lookupResult.parentSymbol,
            identOrOp=lookupResult.identOrOp,
            templateParams=lookupResult.templateParams,
            templateArgs=lookupResult.templateArgs,
            declaration=declaration,
            docname=docname,
        )
        if Symbol.debug_lookup:
            print("      end:   creating candidate symbol")
        return symbol

    if len(withDecl) == 0:
        candSymbol = None
    else:
        candSymbol = makeCandSymbol()

        def handleDuplicateDeclaration(symbol, candSymbol):
            if Symbol.debug_lookup:
                print("      redeclaration")
            # Redeclaration of the same symbol.
            # Let the new one be there, but raise an error to the client
            # so it can use the real symbol as subscope.
            # This will probably result in a duplicate id warning.
            candSymbol.isRedeclaration = True
            raise _DuplicateSymbolError(symbol, declaration)

        if declaration.objectType != "function":
            assert len(withDecl) <= 1
            handleDuplicateDeclaration(withDecl[0], candSymbol)
            # (not reachable)

        # a function, so compare IDs
        candId = declaration.get_newest_id()
        if Symbol.debug_lookup:
            print("      candId:", candId)
        for symbol in withDecl:
            oldId = symbol.declaration.get_newest_id()
            if Symbol.debug_lookup:
                print("      oldId: ", oldId)
            if candId == oldId:
                handleDuplicateDeclaration(symbol, candSymbol)
                # (not reachable)
        # no candidate symbol found with matching ID
    # if there is an empty symbol, fill that one
    if len(noDecl) == 0:
        if Symbol.debug_lookup:
            print(
                "      no match, no empty, candSybmol is not None?:",
                candSymbol is not None,
            )  # NOQA
        if candSymbol is not None:
            return candSymbol
        else:
            return makeCandSymbol()
    else:
        if Symbol.debug_lookup:
            print(
                "      no match, but fill an empty declaration, candSybmol is not None?:",
                candSymbol is not None,
            )  # NOQA
        if candSymbol is not None:
            candSymbol.remove()
        # assert len(noDecl) == 1
        # TODO: enable assertion when we at some point find out how to do cleanup
        # for now, just take the first one, it should work fine ... right?
        symbol = noDecl[0]
        # 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