diff options
author | Jakob Lykke Andersen <Jakob@caput.dk> | 2020-01-24 23:18:48 +0100 |
---|---|---|
committer | Jakob Lykke Andersen <Jakob@caput.dk> | 2020-01-25 11:43:26 +0100 |
commit | 84bd44d04a79e22fa75bbbc9c3b325c2747898d0 (patch) | |
tree | 6880b5dc5c7a096545e25f2535ef1fff256503b8 | |
parent | 80e08fe8fa9c00697d2356b3173f8a7349474a67 (diff) | |
download | sphinx-git-84bd44d04a79e22fa75bbbc9c3b325c2747898d0.tar.gz |
C++, fix cross references in compound directives
Fixes sphinx-doc/sphinx#5078
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | sphinx/domains/cpp.py | 329 | ||||
-rw-r--r-- | tests/roots/test-domain-cpp/multi-decl-lookup.rst | 24 | ||||
-rw-r--r-- | tests/test_domain_cpp.py | 5 |
4 files changed, 301 insertions, 62 deletions
@@ -43,7 +43,10 @@ Features added Bugs fixed ---------- -* C++, fix cross reference lookup in certain cases involving function overloads. +* C++, fix cross reference lookup in certain cases involving + function overloads. +* #5078: C++, fix cross reference lookup when a directive contains multiple + declarations. Testing -------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 47a9d3906..0f70cffbf 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3559,9 +3559,16 @@ class LookupKey: class Symbol: + debug_indent = 0 + debug_indent_string = " " debug_lookup = False debug_show_tree = False + @staticmethod + def debug_print(*args): + print(Symbol.debug_indent_string * Symbol.debug_indent, end="") + print(*args) + def _assert_invariants(self) -> None: if not self.parent: # parent == None means global scope, so declaration means a parent @@ -3584,6 +3591,9 @@ class Symbol: templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction], templateArgs: Any, declaration: ASTDeclaration, docname: str) -> None: self.parent = parent + # declarations in a single directive are linked together + self.siblingAbove = None # type: Symbol + self.siblingBelow = None # type: Symbol self.identOrOp = identOrOp self.templateParams = templateParams # template<templateParams> self.templateArgs = templateArgs # identifier<templateArgs> @@ -3618,6 +3628,9 @@ class Symbol: self._add_template_and_function_params() def _add_template_and_function_params(self): + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_add_template_and_function_params:") # Note: we may be called from _fill_empty, so the symbols we want # to add may actually already be present (as empty symbols). @@ -3647,6 +3660,8 @@ class Symbol: assert not nn.rooted assert len(nn.names) == 1 self._add_symbols(nn, [], decl, self.docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 def remove(self): if self.parent is None: @@ -3656,12 +3671,18 @@ class Symbol: self.parent = None def clear_doc(self, docname: str) -> None: - newChildren = [] + newChildren = [] # type: List[Symbol] for sChild in self._children: sChild.clear_doc(docname) if sChild.declaration and sChild.docname == docname: sChild.declaration = None sChild.docname = None + if sChild.siblingAbove is not None: + sChild.siblingAbove.siblingBelow = sChild.siblingBelow + if sChild.siblingBelow is not None: + sChild.siblingBelow.siblingAbove = sChild.siblingAbove + sChild.siblingAbove = None + sChild.siblingBelow = None newChildren.append(sChild) self._children = newChildren @@ -3719,9 +3740,12 @@ class Symbol: templateShorthand: bool, matchSelf: bool, recurseInAnon: bool, correctPrimaryTemplateArgs: bool ) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_print("_find_first_named_symbol ->") res = self._find_named_symbols(identOrOp, templateParams, templateArgs, templateShorthand, matchSelf, recurseInAnon, - correctPrimaryTemplateArgs) + correctPrimaryTemplateArgs, + searchInSiblings=False) try: return next(res) except StopIteration: @@ -3730,8 +3754,22 @@ class Symbol: def _find_named_symbols(self, identOrOp: Union[ASTIdentifier, ASTOperator], templateParams: Any, templateArgs: ASTTemplateArgs, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool, correctPrimaryTemplateArgs: bool - ) -> Iterator["Symbol"]: + recurseInAnon: bool, correctPrimaryTemplateArgs: bool, + searchInSiblings: bool) -> Iterator["Symbol"]: + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_find_named_symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("templateParams: ", templateParams) + Symbol.debug_print("templateArgs: ", templateArgs) + Symbol.debug_print("templateShorthand: ", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("correctPrimaryTemplateAargs:", correctPrimaryTemplateArgs) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) def isSpecialization(): # the names of the template parameters must be given exactly as args @@ -3783,20 +3821,64 @@ class Symbol: if str(s.templateArgs) != str(templateArgs): return False return True - if matchSelf and matches(self): - yield self - children = self.children_recurse_anon if recurseInAnon else self._children - for s in children: + + def candidates(): + s = self + if Symbol.debug_lookup: + Symbol.debug_print("searching in self:") + print(s.to_string(Symbol.debug_indent + 1), end="") + while True: + if matchSelf: + yield s + if recurseInAnon: + yield from s.children_recurse_anon + else: + yield from s._children + + if s.siblingAbove is None: + break + s = s.siblingAbove + if Symbol.debug_lookup: + Symbol.debug_print("searching in sibling:") + print(s.to_string(Symbol.debug_indent + 1), end="") + + for s in candidates(): + if Symbol.debug_lookup: + Symbol.debug_print("candidate:") + print(s.to_string(Symbol.debug_indent + 1), end="") if matches(s): + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("matches") + Symbol.debug_indent -= 3 yield s + if Symbol.debug_lookup: + Symbol.debug_indent += 2 + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 def _symbol_lookup(self, nestedName: ASTNestedName, templateDecls: List[Any], onMissingQualifiedSymbol: Callable[["Symbol", Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs], "Symbol"], # NOQA strictTemplateParamArgLists: bool, ancestorLookupType: str, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool, correctPrimaryTemplateArgs: bool - ) -> SymbolLookupResult: + recurseInAnon: bool, correctPrimaryTemplateArgs: bool, + searchInSiblings: bool) -> SymbolLookupResult: # ancestorLookupType: if not None, specifies the target type of the lookup + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_symbol_lookup:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("nestedName: ", nestedName) + Symbol.debug_print("templateDecls: ", templateDecls) + Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists) + Symbol.debug_print("ancestorLookupType:", ancestorLookupType) + Symbol.debug_print("templateShorthand: ", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("correctPrimaryTemplateArgs: ", correctPrimaryTemplateArgs) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) if strictTemplateParamArgLists: # Each template argument list must have a template parameter list. @@ -3820,7 +3902,8 @@ class Symbol: while parentSymbol.parent: if parentSymbol.find_identifier(firstName.identOrOp, matchSelf=matchSelf, - recurseInAnon=recurseInAnon): + recurseInAnon=recurseInAnon, + searchInSiblings=searchInSiblings): # if we are in the scope of a constructor but wants to # reference the class we need to walk one extra up if (len(names) == 1 and ancestorLookupType == 'class' and matchSelf and @@ -3831,6 +3914,10 @@ class Symbol: break parentSymbol = parentSymbol.parent + if Symbol.debug_lookup: + Symbol.debug_print("starting point:") + print(parentSymbol.to_string(Symbol.debug_indent + 1), end="") + # and now the actual lookup iTemplateDecl = 0 for name in names[:-1]: @@ -3864,6 +3951,8 @@ class Symbol: symbol = onMissingQualifiedSymbol(parentSymbol, identOrOp, templateParams, templateArgs) if symbol is None: + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return None # We have now matched part of a nested name, and need to match more # so even if we should matchSelf before, we definitely shouldn't @@ -3871,6 +3960,10 @@ class Symbol: matchSelf = False parentSymbol = symbol + if Symbol.debug_lookup: + Symbol.debug_print("handle last name from:") + print(parentSymbol.to_string(Symbol.debug_indent + 1), end="") + # handle the last name name = names[-1] identOrOp = name.identOrOp @@ -3885,7 +3978,11 @@ class Symbol: symbols = parentSymbol._find_named_symbols( identOrOp, templateParams, templateArgs, templateShorthand=templateShorthand, matchSelf=matchSelf, - recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False) + recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False, + searchInSiblings=searchInSiblings) + if Symbol.debug_lookup: + symbols = list(symbols) # type: ignore + Symbol.debug_indent -= 2 return SymbolLookupResult(symbols, parentSymbol, identOrOp, templateParams, templateArgs) @@ -3895,21 +3992,26 @@ class Symbol: # be an actual declaration. if Symbol.debug_lookup: - print("_add_symbols:") - print(" tdecls:", templateDecls) - print(" nn: ", nestedName) - print(" decl: ", declaration) - print(" doc: ", docname) + Symbol.debug_indent += 1 + Symbol.debug_print("_add_symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("tdecls:", templateDecls) + Symbol.debug_print("nn: ", nestedName) + Symbol.debug_print("decl: ", declaration) + Symbol.debug_print("doc: ", docname) def onMissingQualifiedSymbol(parentSymbol: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], templateParams: Any, templateArgs: ASTTemplateArgs ) -> "Symbol": if Symbol.debug_lookup: - print(" _add_symbols, onMissingQualifiedSymbol:") - print(" templateParams:", templateParams) - print(" identOrOp: ", identOrOp) - print(" templateARgs: ", templateArgs) + Symbol.debug_indent += 1 + Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:") + Symbol.debug_indent += 1 + Symbol.debug_print("templateParams:", templateParams) + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("templateARgs: ", templateArgs) + Symbol.debug_indent -= 2 return Symbol(parent=parentSymbol, identOrOp=identOrOp, templateParams=templateParams, templateArgs=templateArgs, declaration=None, @@ -3922,32 +4024,40 @@ class Symbol: templateShorthand=False, matchSelf=False, recurseInAnon=True, - correctPrimaryTemplateArgs=True) + correctPrimaryTemplateArgs=True, + searchInSiblings=False) 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.debug_print("_add_symbols, result, no symbol:") + Symbol.debug_indent += 1 + Symbol.debug_print("templateParams:", lookupResult.templateParams) + Symbol.debug_print("identOrOp: ", lookupResult.identOrOp) + Symbol.debug_print("templateArgs: ", lookupResult.templateArgs) + Symbol.debug_print("declaration: ", declaration) + Symbol.debug_print("docname: ", docname) + Symbol.debug_indent -= 1 symbol = Symbol(parent=lookupResult.parentSymbol, identOrOp=lookupResult.identOrOp, templateParams=lookupResult.templateParams, templateArgs=lookupResult.templateArgs, declaration=declaration, docname=docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return symbol if Symbol.debug_lookup: - print(" _add_symbols, result, symbols:") - print(" number symbols:", len(symbols)) + Symbol.debug_print("_add_symbols, result, symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("number symbols:", len(symbols)) + Symbol.debug_indent -= 1 if not declaration: if Symbol.debug_lookup: - print(" no delcaration") + Symbol.debug_print("no delcaration") + Symbol.debug_indent -= 2 # good, just a scope creation # TODO: what if we have more than one symbol? return symbols[0] @@ -3963,9 +4073,9 @@ class Symbol: else: withDecl.append(s) if Symbol.debug_lookup: - print(" #noDecl: ", len(noDecl)) - print(" #withDecl:", len(withDecl)) - print(" #dupDecl: ", len(dupDecl)) + Symbol.debug_print("#noDecl: ", len(noDecl)) + Symbol.debug_print("#withDecl:", len(withDecl)) + Symbol.debug_print("#dupDecl: ", len(dupDecl)) # With partial builds we may start with a large symbol tree stripped of declarations. # Essentially any combination of noDecl, withDecl, and dupDecls seems possible. # TODO: make partial builds fully work. What should happen when the primary symbol gets @@ -3976,7 +4086,7 @@ class Symbol: # otherwise there should be only one symbol with a declaration. def makeCandSymbol(): if Symbol.debug_lookup: - print(" begin: creating candidate symbol") + Symbol.debug_print("begin: creating candidate symbol") symbol = Symbol(parent=lookupResult.parentSymbol, identOrOp=lookupResult.identOrOp, templateParams=lookupResult.templateParams, @@ -3984,7 +4094,7 @@ class Symbol: declaration=declaration, docname=docname) if Symbol.debug_lookup: - print(" end: creating candidate symbol") + Symbol.debug_print("end: creating candidate symbol") return symbol if len(withDecl) == 0: candSymbol = None @@ -3993,7 +4103,10 @@ class Symbol: def handleDuplicateDeclaration(symbol, candSymbol): if Symbol.debug_lookup: - print(" redeclaration") + Symbol.debug_indent += 1 + Symbol.debug_print("redeclaration") + Symbol.debug_indent -= 1 + Symbol.debug_indent -= 2 # 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. @@ -4009,11 +4122,11 @@ class Symbol: # a function, so compare IDs candId = declaration.get_newest_id() if Symbol.debug_lookup: - print(" candId:", candId) + Symbol.debug_print("candId:", candId) for symbol in withDecl: oldId = symbol.declaration.get_newest_id() if Symbol.debug_lookup: - print(" oldId: ", oldId) + Symbol.debug_print("oldId: ", oldId) if candId == oldId: handleDuplicateDeclaration(symbol, candSymbol) # (not reachable) @@ -4021,14 +4134,16 @@ class Symbol: # 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 + Symbol.debug_print("no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_indent -= 2 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 + Symbol.debug_print("no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_indent -= 2 if candSymbol is not None: candSymbol.remove() # assert len(noDecl) == 1 @@ -4045,6 +4160,9 @@ class Symbol: def merge_with(self, other: "Symbol", docnames: List[str], env: "BuildEnvironment") -> None: + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("merge_with:") assert other is not None for otherChild in other._children: ourChild = self._find_first_named_symbol( @@ -4074,17 +4192,28 @@ class Symbol: # just ignore it, right? pass ourChild.merge_with(otherChild, docnames, env) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 def add_name(self, nestedName: ASTNestedName, templatePrefix: ASTTemplateDeclarationPrefix = None) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("add_name:") if templatePrefix: templateDecls = templatePrefix.templates else: templateDecls = [] - return self._add_symbols(nestedName, templateDecls, - declaration=None, docname=None) + res = self._add_symbols(nestedName, templateDecls, + declaration=None, docname=None) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 + return res def add_declaration(self, declaration: ASTDeclaration, docname: str) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("add_declaration:") assert declaration assert docname nestedName = declaration.name @@ -4092,19 +4221,47 @@ class Symbol: templateDecls = declaration.templatePrefix.templates else: templateDecls = [] - return self._add_symbols(nestedName, templateDecls, declaration, docname) + res = self._add_symbols(nestedName, templateDecls, declaration, docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 + return res def find_identifier(self, identOrOp: Union[ASTIdentifier, ASTOperator], - matchSelf: bool, recurseInAnon: bool) -> "Symbol": - if matchSelf and self.identOrOp == identOrOp: - return self - children = self.children_recurse_anon if recurseInAnon else self._children - for s in children: - if s.identOrOp == identOrOp: - return s + matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool + ) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_identifier:") + Symbol.debug_indent += 1 + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("searchInSiblings:", searchInSiblings) + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_indent -= 2 + current = self + while current is not None: + if Symbol.debug_lookup: + Symbol.debug_indent += 2 + Symbol.debug_print("trying:") + print(current.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_indent -= 2 + if matchSelf and current.identOrOp == identOrOp: + return current + children = current.children_recurse_anon if recurseInAnon else current._children + for s in children: + if s.identOrOp == identOrOp: + return s + if not searchInSiblings: + break + current = current.siblingAbove return None - def direct_lookup(self, key: List[Tuple[ASTNestedNameElement, Any]]) -> "Symbol": + def direct_lookup(self, key: "LookupKey") -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("direct_lookup:") + Symbol.debug_indent += 1 s = self for name, templateParams, id_ in key.data: if id_ is not None: @@ -4125,14 +4282,39 @@ class Symbol: matchSelf=False, recurseInAnon=False, correctPrimaryTemplateArgs=False) + if Symbol.debug_lookup: + Symbol.debug_print("name: ", name) + Symbol.debug_print("templateParams:", templateParams) + Symbol.debug_print("id: ", id_) + if s is not None: + print(s.to_string(Symbol.debug_indent + 1), end="") + else: + Symbol.debug_print("not found") if s is None: + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return None + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return s def find_name(self, nestedName: ASTNestedName, templateDecls: List[Any], typ: str, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool) -> List["Symbol"]: + recurseInAnon: bool, searchInSiblings: bool) -> List["Symbol"]: # templateShorthand: missing template parameter lists for templates is ok + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_name:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("nestedName: ", nestedName) + Symbol.debug_print("templateDecls: ", templateDecls) + Symbol.debug_print("typ: ", typ) + Symbol.debug_print("templateShorthand:", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) def onMissingQualifiedSymbol(parentSymbol: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], @@ -4151,13 +4333,18 @@ class Symbol: templateShorthand=templateShorthand, matchSelf=matchSelf, recurseInAnon=recurseInAnon, - correctPrimaryTemplateArgs=False) + correctPrimaryTemplateArgs=False, + searchInSiblings=searchInSiblings) if lookupResult is None: # if it was a part of the qualification that could not be found + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return None res = list(lookupResult.symbols) if len(res) != 0: + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return res # try without template params and args @@ -4165,6 +4352,8 @@ class Symbol: lookupResult.identOrOp, None, None, templateShorthand=templateShorthand, matchSelf=matchSelf, recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False) + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 if symbol is not None: return [symbol] else: @@ -4173,6 +4362,9 @@ class Symbol: def find_declaration(self, declaration: ASTDeclaration, typ: str, templateShorthand: bool, matchSelf: bool, recurseInAnon: bool) -> "Symbol": # templateShorthand: missing template parameter lists for templates is ok + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_declaration:") nestedName = declaration.name if declaration.templatePrefix: templateDecls = declaration.templatePrefix.templates @@ -4192,8 +4384,10 @@ class Symbol: templateShorthand=templateShorthand, matchSelf=matchSelf, recurseInAnon=recurseInAnon, - correctPrimaryTemplateArgs=False) - + correctPrimaryTemplateArgs=False, + searchInSiblings=False) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 if lookupResult is None: return None @@ -4219,14 +4413,14 @@ class Symbol: return None def to_string(self, indent: int) -> str: - res = ['\t' * indent] + res = [Symbol.debug_indent_string * indent] if not self.parent: res.append('::') else: if self.templateParams: res.append(str(self.templateParams)) res.append('\n') - res.append('\t' * indent) + res.append(Symbol.debug_indent_string * indent) if self.identOrOp: res.append(str(self.identOrOp)) else: @@ -6211,7 +6405,8 @@ class CPPObject(ObjectDescription): return targetSymbol = parentSymbol.parent - s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True) + s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True, + searchInSiblings=False) if s is not None: # something is already declared with that name return @@ -6326,6 +6521,10 @@ class CPPObject(ObjectDescription): symbol = parentSymbol.add_name(name) env.temp_data['cpp:last_symbol'] = symbol return [] + # When multiple declarations are made in the same directive + # they need to know about each other to provide symbol lookup for function parameters. + # We use last_symbol to store the latest added declaration in a directive. + env.temp_data['cpp:last_symbol'] = None return super().run() def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: @@ -6346,6 +6545,13 @@ class CPPObject(ObjectDescription): try: symbol = parentSymbol.add_declaration(ast, docname=self.env.docname) + # append the new declaration to the sibling list + assert symbol.siblingAbove is None + assert symbol.siblingBelow is None + symbol.siblingAbove = self.env.temp_data['cpp:last_symbol'] + if symbol.siblingAbove is not None: + assert symbol.siblingAbove.siblingBelow is None + symbol.siblingAbove.siblingBelow = symbol self.env.temp_data['cpp:last_symbol'] = symbol except _DuplicateSymbolError as e: # Assume we are actually in the old symbol, @@ -6878,9 +7084,12 @@ class CPPDomain(Domain): templateDecls = ns.templatePrefix.templates else: templateDecls = [] + # let's be conservative with the sibling lookup for now + searchInSiblings = (not name.rooted) and len(name.names) == 1 symbols = parentSymbol.find_name(name, templateDecls, typ, templateShorthand=True, - matchSelf=True, recurseInAnon=True) + matchSelf=True, recurseInAnon=True, + searchInSiblings=searchInSiblings) # just refer to the arbitrarily first symbol s = None if symbols is None else symbols[0] else: diff --git a/tests/roots/test-domain-cpp/multi-decl-lookup.rst b/tests/roots/test-domain-cpp/multi-decl-lookup.rst new file mode 100644 index 000000000..9706d1822 --- /dev/null +++ b/tests/roots/test-domain-cpp/multi-decl-lookup.rst @@ -0,0 +1,24 @@ +.. default-domain:: cpp + +.. namespace:: multi_decl_lookup + +.. function:: void f1(int a) + void f1(double b) + + - a: :var:`a` + - b: :var:`b` + +.. function:: template<typename T> void f2(int a) + template<typename U> void f2(double b) + + - T: :type:`T` + - U: :type:`U` + + +.. class:: template<typename T> A + template<typename U> B + + .. function:: void f3() + + - T: :type:`T` + - U: :type:`U` diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index b1796cdff..7d9c89b7a 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -796,12 +796,15 @@ def filter_warnings(warning, file): return res -@pytest.mark.sphinx(testroot='domain-cpp') +@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True}) def test_build_domain_cpp_multi_decl_lookup(app, status, warning): app.builder.build_all() ws = filter_warnings(warning, "lookup-key-overload") assert len(ws) == 0 + ws = filter_warnings(warning, "multi-decl-lookup") + assert len(ws) == 0 + @pytest.mark.sphinx(testroot='domain-cpp') def test_build_domain_cpp_misuse_of_roles(app, status, warning): |