summaryrefslogtreecommitdiff
path: root/sphinx/domains/cpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r--sphinx/domains/cpp.py502
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,
}