diff options
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r-- | sphinx/domains/cpp.py | 502 |
1 files changed, 391 insertions, 111 deletions
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index d8eec6330..93cf2e646 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -367,6 +367,8 @@ _keywords = [ _max_id = 4 _id_prefix = [None, '', '_CPPv2', '_CPPv3', '_CPPv4'] +# Ids are used in lookup keys which are used across pickled files, +# so when _max_id changes, make sure to update the ENV_VERSION. # ------------------------------------------------------------------------------ # Id v1 constants @@ -1790,7 +1792,8 @@ class ASTTemplateIntroduction(ASTBase): class ASTTemplateDeclarationPrefix(ASTBase): - def __init__(self, templates: List[Any]) -> None: + def __init__(self, templates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]])\ + -> None: # templates is None means it's an explicit instantiation of a variable self.templates = templates @@ -3547,10 +3550,25 @@ class SymbolLookupResult: self.templateArgs = templateArgs +class LookupKey: + def __init__(self, data: List[Tuple[ASTNestedNameElement, + Union[ASTTemplateParams, + ASTTemplateIntroduction], + str]]) -> None: + self.data = data + + 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 @@ -3570,9 +3588,12 @@ class Symbol: return super().__setattr__(key, value) def __init__(self, parent: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], - templateParams: Any, templateArgs: Any, declaration: ASTDeclaration, - docname: str) -> None: + 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> @@ -3607,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). @@ -3636,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: @@ -3645,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 @@ -3669,7 +3701,11 @@ class Symbol: yield from c.children_recurse_anon - def get_lookup_key(self) -> List[Tuple[ASTNestedNameElement, Any]]: + def get_lookup_key(self) -> "LookupKey": + # The pickle files for the environment and for each document are distinct. + # The environment has all the symbols, but the documents has xrefs that + # must know their scope. A lookup key is essentially a specification of + # how to find a specific symbol. symbols = [] s = self while s.parent: @@ -3679,14 +3715,23 @@ class Symbol: key = [] for s in symbols: nne = ASTNestedNameElement(s.identOrOp, s.templateArgs) - key.append((nne, s.templateParams)) - return key + if s.declaration is not None: + key.append((nne, s.templateParams, s.declaration.get_newest_id())) + else: + key.append((nne, s.templateParams, None)) + return LookupKey(key) def get_full_nested_name(self) -> ASTNestedName: + symbols = [] + s = self + while s.parent: + symbols.append(s) + s = s.parent + symbols.reverse() names = [] templates = [] - for nne, templateParams in self.get_lookup_key(): - names.append(nne) + for s in symbols: + names.append(ASTNestedNameElement(s.identOrOp, s.templateArgs)) templates.append(False) return ASTNestedName(names, templates, rooted=False) @@ -3695,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: @@ -3706,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 @@ -3759,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. @@ -3796,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 @@ -3807,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]: @@ -3840,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 @@ -3847,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 @@ -3861,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) @@ -3871,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, @@ -3898,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] @@ -3939,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 @@ -3952,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, @@ -3960,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 @@ -3969,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. @@ -3985,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) @@ -3997,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 @@ -4021,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( @@ -4050,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 @@ -4068,37 +4221,105 @@ 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 in key: - identOrOp = name.identOrOp - templateArgs = name.templateArgs - s = s._find_first_named_symbol(identOrOp, - templateParams, templateArgs, - templateShorthand=False, - matchSelf=False, - recurseInAnon=False, - correctPrimaryTemplateArgs=False) - if not s: + for name, templateParams, id_ in key.data: + if id_ is not None: + res = None + for cand in s._children: + if cand.declaration is None: + continue + if cand.declaration.get_newest_id() == id_: + res = cand + break + s = res + else: + identOrOp = name.identOrOp + templateArgs = name.templateArgs + s = s._find_first_named_symbol(identOrOp, + templateParams, templateArgs, + templateShorthand=False, + 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) -> Tuple[List["Symbol"], str]: # templateShorthand: missing template parameter lists for templates is ok + # If the first component is None, + # then the second component _may_ be a string explaining why. + 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) + + class QualifiedSymbolIsTemplateParam(Exception): + pass def onMissingQualifiedSymbol(parentSymbol: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], @@ -4108,37 +4329,58 @@ class Symbol: # Though, the correctPrimaryTemplateArgs does # that for primary templates. # Is there another case where it would be good? + if parentSymbol.declaration is not None: + if parentSymbol.declaration.objectType == 'templateParam': + raise QualifiedSymbolIsTemplateParam() return None - lookupResult = self._symbol_lookup(nestedName, templateDecls, - onMissingQualifiedSymbol, - strictTemplateParamArgLists=False, - ancestorLookupType=typ, - templateShorthand=templateShorthand, - matchSelf=matchSelf, - recurseInAnon=recurseInAnon, - correctPrimaryTemplateArgs=False) + try: + lookupResult = self._symbol_lookup(nestedName, templateDecls, + onMissingQualifiedSymbol, + strictTemplateParamArgLists=False, + ancestorLookupType=typ, + templateShorthand=templateShorthand, + matchSelf=matchSelf, + recurseInAnon=recurseInAnon, + correctPrimaryTemplateArgs=False, + searchInSiblings=searchInSiblings) + except QualifiedSymbolIsTemplateParam: + return None, "templateParamInQualified" + if lookupResult is None: # if it was a part of the qualification that could not be found - return None + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 + return None, None res = list(lookupResult.symbols) if len(res) != 0: - return res + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 + return res, None + + if lookupResult.parentSymbol.declaration is not None: + if lookupResult.parentSymbol.declaration.objectType == 'templateParam': + return None, "templateParamInQualified" # try without template params and args symbol = lookupResult.parentSymbol._find_first_named_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] + return [symbol], None else: - return None + return None, None 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 @@ -4158,8 +4400,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 @@ -4185,14 +4429,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: @@ -5931,14 +6175,15 @@ class DefinitionParser: def _parse_template_declaration_prefix(self, objectType: str ) -> ASTTemplateDeclarationPrefix: - templates = [] # type: List[str] + templates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] while 1: self.skip_ws() # the saved position is only used to provide a better error message + params = None # type: Union[ASTTemplateParams, ASTTemplateIntroduction] pos = self.pos if self.skip_word("template"): try: - params = self._parse_template_parameter_list() # type: Any + params = self._parse_template_parameter_list() except DefinitionError as e: if objectType == 'member' and len(templates) == 0: return ASTTemplateDeclarationPrefix(None) @@ -5990,7 +6235,7 @@ class DefinitionParser: msg += str(nestedName) self.warn(msg) - newTemplates = [] + newTemplates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] for i in range(numExtra): newTemplates.append(ASTTemplateParams([])) if templatePrefix and not isMemberInstantiation: @@ -6176,7 +6421,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 @@ -6291,6 +6537,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: @@ -6311,6 +6561,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, @@ -6329,10 +6586,10 @@ class CPPObject(ObjectDescription): return ast def before_content(self) -> None: - lastSymbol = self.env.temp_data['cpp:last_symbol'] + lastSymbol = self.env.temp_data['cpp:last_symbol'] # type: Symbol assert lastSymbol self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol'] - self.oldParentKey = self.env.ref_context['cpp:parent_key'] + self.oldParentKey = self.env.ref_context['cpp:parent_key'] # type: LookupKey self.env.temp_data['cpp:parent_symbol'] = lastSymbol self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key() @@ -6521,8 +6778,8 @@ class AliasTransform(SphinxTransform): node.replace_self(signode) continue - rootSymbol = self.env.domains['cpp'].data['root_symbol'] - parentSymbol = rootSymbol.direct_lookup(parentKey) + rootSymbol = self.env.domains['cpp'].data['root_symbol'] # type: Symbol + parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol if not parentSymbol: print("Target: ", sig) print("ParentKey: ", parentKey) @@ -6537,9 +6794,13 @@ class AliasTransform(SphinxTransform): templateDecls = ns.templatePrefix.templates else: templateDecls = [] - symbols = parentSymbol.find_name(name, templateDecls, 'any', - templateShorthand=True, - matchSelf=True, recurseInAnon=True) + symbols, failReason = parentSymbol.find_name( + nestedName=name, + templateDecls=templateDecls, + typ='any', + templateShorthand=True, + matchSelf=True, recurseInAnon=True, + searchInSiblings=False) if symbols is None: symbols = [] else: @@ -6824,13 +7085,13 @@ class CPPDomain(Domain): t, ex = findWarning(e) warner.warn('Unparseable C++ cross-reference: %r\n%s' % (t, ex)) return None, None - parentKey = node.get("cpp:parent_key", None) + parentKey = node.get("cpp:parent_key", None) # type: LookupKey rootSymbol = self.data['root_symbol'] if parentKey: - parentSymbol = rootSymbol.direct_lookup(parentKey) + parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol if not parentSymbol: print("Target: ", target) - print("ParentKey: ", parentKey) + print("ParentKey: ", parentKey.data) print(rootSymbol.dump(1)) assert parentSymbol # should be there else: @@ -6843,11 +7104,23 @@ class CPPDomain(Domain): templateDecls = ns.templatePrefix.templates else: templateDecls = [] - symbols = parentSymbol.find_name(name, templateDecls, typ, - templateShorthand=True, - matchSelf=True, recurseInAnon=True) - # just refer to the arbitrarily first symbol - s = None if symbols is None else symbols[0] + # let's be conservative with the sibling lookup for now + searchInSiblings = (not name.rooted) and len(name.names) == 1 + symbols, failReason = parentSymbol.find_name( + name, templateDecls, typ, + templateShorthand=True, + matchSelf=True, recurseInAnon=True, + searchInSiblings=searchInSiblings) + if symbols is None: + if typ == 'identifier': + if failReason == 'templateParamInQualified': + # this is an xref we created as part of a signature, + # so don't warn for names nested in template parameters + raise NoUri(str(name), typ) + s = None + else: + # just refer to the arbitrarily first symbol + s = symbols[0] else: decl = ast # type: ASTDeclaration name = decl.name @@ -6862,23 +7135,30 @@ class CPPDomain(Domain): if typ.startswith('cpp:'): typ = typ[4:] + origTyp = typ if typ == 'func': typ = 'function' + if typ == 'struct': + typ = 'class' declTyp = s.declaration.objectType def checkType(): if typ == 'any' or typ == 'identifier': return True if declTyp == 'templateParam': + # TODO: perhaps this should be strengthened one day return True + if declTyp == 'functionParam': + if typ == 'var' or typ == 'member': + return True objtypes = self.objtypes_for_role(typ) if objtypes: return declTyp in objtypes - print("Type is %s, declType is %s" % (typ, declTyp)) + print("Type is %s (originally: %s), declType is %s" % (typ, origTyp, declTyp)) assert False if not checkType(): warner.warn("cpp:%s targets a %s (%s)." - % (typ, s.declaration.objectType, + % (origTyp, s.declaration.objectType, s.get_full_nested_name())) declaration = s.declaration @@ -6970,8 +7250,8 @@ class CPPDomain(Domain): target = node.get('reftarget', None) if target is None: return None - parentKey = node.get("cpp:parent_key", None) - if parentKey is None or len(parentKey) <= 0: + parentKey = node.get("cpp:parent_key", None) # type: LookupKey + if parentKey is None or len(parentKey.data) <= 0: return None rootSymbol = self.data['root_symbol'] @@ -6989,7 +7269,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: return { 'version': 'builtin', - 'env_version': 1, + 'env_version': 2, 'parallel_read_safe': True, 'parallel_write_safe': True, } |