You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1093 lines
48 KiB
1093 lines
48 KiB
8 months ago
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TYPE_CHECKING, Any, Callable, NoReturn
|
||
|
|
||
|
from sphinx.domains.cpp._ast import (
|
||
|
ASTDeclaration,
|
||
|
ASTIdentifier,
|
||
|
ASTNestedName,
|
||
|
ASTNestedNameElement,
|
||
|
ASTOperator,
|
||
|
ASTTemplateArgs,
|
||
|
ASTTemplateDeclarationPrefix,
|
||
|
ASTTemplateIntroduction,
|
||
|
ASTTemplateParams,
|
||
|
)
|
||
|
from sphinx.locale import __
|
||
|
from sphinx.util import logging
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from collections.abc import Iterator
|
||
|
|
||
|
from sphinx.environment import BuildEnvironment
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class _DuplicateSymbolError(Exception):
|
||
|
def __init__(self, symbol: Symbol, declaration: ASTDeclaration) -> None:
|
||
|
assert symbol
|
||
|
assert declaration
|
||
|
self.symbol = symbol
|
||
|
self.declaration = declaration
|
||
|
|
||
|
def __str__(self) -> str:
|
||
|
return "Internal C++ duplicate symbol error:\n%s" % self.symbol.dump(0)
|
||
|
|
||
|
|
||
|
class SymbolLookupResult:
|
||
|
def __init__(self, symbols: Iterator[Symbol], parentSymbol: Symbol,
|
||
|
identOrOp: ASTIdentifier | ASTOperator, templateParams: Any,
|
||
|
templateArgs: ASTTemplateArgs) -> None:
|
||
|
self.symbols = symbols
|
||
|
self.parentSymbol = parentSymbol
|
||
|
self.identOrOp = identOrOp
|
||
|
self.templateParams = templateParams
|
||
|
self.templateArgs = templateArgs
|
||
|
|
||
|
|
||
|
class LookupKey:
|
||
|
def __init__(self, data: list[tuple[ASTNestedNameElement,
|
||
|
ASTTemplateParams | ASTTemplateIntroduction,
|
||
|
str]]) -> None:
|
||
|
self.data = data
|
||
|
|
||
|
|
||
|
def _is_specialization(templateParams: ASTTemplateParams | ASTTemplateIntroduction,
|
||
|
templateArgs: ASTTemplateArgs) -> bool:
|
||
|
# Checks if `templateArgs` does not exactly match `templateParams`.
|
||
|
# the names of the template parameters must be given exactly as args
|
||
|
# and params that are packs must in the args be the name expanded
|
||
|
if len(templateParams.params) != len(templateArgs.args):
|
||
|
return True
|
||
|
# having no template params and no arguments is also a specialization
|
||
|
if len(templateParams.params) == 0:
|
||
|
return True
|
||
|
for i in range(len(templateParams.params)):
|
||
|
param = templateParams.params[i]
|
||
|
arg = templateArgs.args[i]
|
||
|
# TODO: doing this by string manipulation is probably not the most efficient
|
||
|
paramName = str(param.name)
|
||
|
argTxt = str(arg)
|
||
|
isArgPackExpansion = argTxt.endswith('...')
|
||
|
if param.isPack != isArgPackExpansion:
|
||
|
return True
|
||
|
argName = argTxt[:-3] if isArgPackExpansion else argTxt
|
||
|
if paramName != argName:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
class Symbol:
|
||
|
debug_indent = 0
|
||
|
debug_indent_string = " "
|
||
|
debug_lookup = False # overridden by the corresponding config value
|
||
|
debug_show_tree = False # overridden by the corresponding config value
|
||
|
|
||
|
def __copy__(self) -> NoReturn:
|
||
|
raise AssertionError # shouldn't happen
|
||
|
|
||
|
def __deepcopy__(self, memo: Any) -> Symbol:
|
||
|
if self.parent:
|
||
|
raise AssertionError # shouldn't happen
|
||
|
# the domain base class makes a copy of the initial data, which is fine
|
||
|
return Symbol(None, None, None, None, None, None, None)
|
||
|
|
||
|
@staticmethod
|
||
|
def debug_print(*args: Any) -> None:
|
||
|
logger.debug(Symbol.debug_indent_string * Symbol.debug_indent, end="")
|
||
|
logger.debug(*args)
|
||
|
|
||
|
def _assert_invariants(self) -> None:
|
||
|
if not self.parent:
|
||
|
# parent == None means global scope, so declaration means a parent
|
||
|
assert not self.identOrOp
|
||
|
assert not self.templateParams
|
||
|
assert not self.templateArgs
|
||
|
assert not self.declaration
|
||
|
assert not self.docname
|
||
|
else:
|
||
|
if self.declaration:
|
||
|
assert self.docname
|
||
|
|
||
|
def __setattr__(self, key: str, value: Any) -> None:
|
||
|
if key == "children":
|
||
|
raise AssertionError
|
||
|
return super().__setattr__(key, value)
|
||
|
|
||
|
def __init__(self, parent: Symbol | None,
|
||
|
identOrOp: ASTIdentifier | ASTOperator | None,
|
||
|
templateParams: ASTTemplateParams | ASTTemplateIntroduction | None,
|
||
|
templateArgs: Any, declaration: ASTDeclaration | None,
|
||
|
docname: str | None, line: int | None) -> None:
|
||
|
self.parent = parent
|
||
|
# declarations in a single directive are linked together
|
||
|
self.siblingAbove: Symbol | None = None
|
||
|
self.siblingBelow: Symbol | None = None
|
||
|
self.identOrOp = identOrOp
|
||
|
# Ensure the same symbol for `A` is created for:
|
||
|
#
|
||
|
# .. cpp:class:: template <typename T> class A
|
||
|
#
|
||
|
# and
|
||
|
#
|
||
|
# .. cpp:function:: template <typename T> int A<T>::foo()
|
||
|
if (templateArgs is not None and
|
||
|
not _is_specialization(templateParams, templateArgs)):
|
||
|
templateArgs = None
|
||
|
self.templateParams = templateParams # template<templateParams>
|
||
|
self.templateArgs = templateArgs # identifier<templateArgs>
|
||
|
self.declaration = declaration
|
||
|
self.docname = docname
|
||
|
self.line = line
|
||
|
self.isRedeclaration = False
|
||
|
self._assert_invariants()
|
||
|
|
||
|
# Remember to modify Symbol.remove if modifications to the parent change.
|
||
|
self._children: list[Symbol] = []
|
||
|
self._anonChildren: list[Symbol] = []
|
||
|
# note: _children includes _anonChildren
|
||
|
if self.parent:
|
||
|
self.parent._children.append(self)
|
||
|
if self.declaration:
|
||
|
self.declaration.symbol = self
|
||
|
|
||
|
# Do symbol addition after self._children has been initialised.
|
||
|
self._add_template_and_function_params()
|
||
|
|
||
|
def _fill_empty(self, declaration: ASTDeclaration, docname: str, line: int) -> None:
|
||
|
self._assert_invariants()
|
||
|
assert self.declaration is None
|
||
|
assert self.docname is None
|
||
|
assert self.line is None
|
||
|
assert declaration is not None
|
||
|
assert docname is not None
|
||
|
assert line is not None
|
||
|
self.declaration = declaration
|
||
|
self.declaration.symbol = self
|
||
|
self.docname = docname
|
||
|
self.line = line
|
||
|
self._assert_invariants()
|
||
|
# and symbol addition should be done as well
|
||
|
self._add_template_and_function_params()
|
||
|
|
||
|
def _add_template_and_function_params(self) -> None:
|
||
|
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).
|
||
|
|
||
|
# add symbols for the template params
|
||
|
if self.templateParams:
|
||
|
for tp in self.templateParams.params:
|
||
|
if not tp.get_identifier():
|
||
|
continue
|
||
|
# only add a declaration if we our self are from a declaration
|
||
|
if self.declaration:
|
||
|
decl = ASTDeclaration(objectType='templateParam', declaration=tp)
|
||
|
else:
|
||
|
decl = None
|
||
|
nne = ASTNestedNameElement(tp.get_identifier(), None)
|
||
|
nn = ASTNestedName([nne], [False], rooted=False)
|
||
|
self._add_symbols(nn, [], decl, self.docname, self.line)
|
||
|
# add symbols for function parameters, if any
|
||
|
if self.declaration is not None and self.declaration.function_params is not None:
|
||
|
for fp in self.declaration.function_params:
|
||
|
if fp.arg is None:
|
||
|
continue
|
||
|
nn = fp.arg.name
|
||
|
if nn is None:
|
||
|
continue
|
||
|
# (comparing to the template params: we have checked that we are a declaration)
|
||
|
decl = ASTDeclaration(objectType='functionParam', declaration=fp)
|
||
|
assert not nn.rooted
|
||
|
assert len(nn.names) == 1
|
||
|
self._add_symbols(nn, [], decl, self.docname, self.line)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
|
||
|
def remove(self) -> None:
|
||
|
if self.parent is None:
|
||
|
return
|
||
|
assert self in self.parent._children
|
||
|
self.parent._children.remove(self)
|
||
|
self.parent = None
|
||
|
|
||
|
def clear_doc(self, docname: str) -> None:
|
||
|
newChildren: list[Symbol] = []
|
||
|
for sChild in self._children:
|
||
|
sChild.clear_doc(docname)
|
||
|
if sChild.declaration and sChild.docname == docname:
|
||
|
sChild.declaration = None
|
||
|
sChild.docname = None
|
||
|
sChild.line = 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
|
||
|
|
||
|
def get_all_symbols(self) -> Iterator[Any]:
|
||
|
yield self
|
||
|
for sChild in self._children:
|
||
|
yield from sChild.get_all_symbols()
|
||
|
|
||
|
@property
|
||
|
def children_recurse_anon(self) -> Iterator[Symbol]:
|
||
|
for c in self._children:
|
||
|
yield c
|
||
|
if not c.identOrOp.is_anon():
|
||
|
continue
|
||
|
|
||
|
yield from c.children_recurse_anon
|
||
|
|
||
|
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:
|
||
|
symbols.append(s)
|
||
|
s = s.parent
|
||
|
symbols.reverse()
|
||
|
key = []
|
||
|
for s in symbols:
|
||
|
nne = ASTNestedNameElement(s.identOrOp, s.templateArgs)
|
||
|
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 s in symbols:
|
||
|
names.append(ASTNestedNameElement(s.identOrOp, s.templateArgs))
|
||
|
templates.append(False)
|
||
|
return ASTNestedName(names, templates, rooted=False)
|
||
|
|
||
|
def _find_first_named_symbol(self, identOrOp: ASTIdentifier | ASTOperator,
|
||
|
templateParams: ASTTemplateParams | ASTTemplateIntroduction,
|
||
|
templateArgs: ASTTemplateArgs | None,
|
||
|
templateShorthand: bool, matchSelf: bool,
|
||
|
recurseInAnon: bool, correctPrimaryTemplateArgs: bool,
|
||
|
) -> Symbol | None:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("_find_first_named_symbol ->")
|
||
|
res = self._find_named_symbols(identOrOp, templateParams, templateArgs,
|
||
|
templateShorthand, matchSelf, recurseInAnon,
|
||
|
correctPrimaryTemplateArgs,
|
||
|
searchInSiblings=False)
|
||
|
try:
|
||
|
return next(res)
|
||
|
except StopIteration:
|
||
|
return None
|
||
|
|
||
|
def _find_named_symbols(self, identOrOp: ASTIdentifier | ASTOperator,
|
||
|
templateParams: ASTTemplateParams | ASTTemplateIntroduction,
|
||
|
templateArgs: ASTTemplateArgs,
|
||
|
templateShorthand: bool, matchSelf: bool,
|
||
|
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:")
|
||
|
logger.debug(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)
|
||
|
|
||
|
if correctPrimaryTemplateArgs:
|
||
|
if templateParams is not None and templateArgs is not None:
|
||
|
# If both are given, but it's not a specialization, then do lookup as if
|
||
|
# there is no argument list.
|
||
|
# For example: template<typename T> int A<T>::var;
|
||
|
if not _is_specialization(templateParams, templateArgs):
|
||
|
templateArgs = None
|
||
|
|
||
|
def matches(s: Symbol) -> bool:
|
||
|
if s.identOrOp != identOrOp:
|
||
|
return False
|
||
|
if (s.templateParams is None) != (templateParams is None):
|
||
|
if templateParams is not None:
|
||
|
# we query with params, they must match params
|
||
|
return False
|
||
|
if not templateShorthand:
|
||
|
# we don't query with params, and we do care about them
|
||
|
return False
|
||
|
if templateParams:
|
||
|
# TODO: do better comparison
|
||
|
if str(s.templateParams) != str(templateParams):
|
||
|
return False
|
||
|
if (s.templateArgs is None) != (templateArgs is None):
|
||
|
return False
|
||
|
if s.templateArgs:
|
||
|
# TODO: do better comparison
|
||
|
if str(s.templateArgs) != str(templateArgs):
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def candidates() -> Iterator[Symbol]:
|
||
|
s = self
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("searching in self:")
|
||
|
logger.debug(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:")
|
||
|
logger.debug(s.to_string(Symbol.debug_indent + 1), end="")
|
||
|
|
||
|
for s in candidates():
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("candidate:")
|
||
|
logger.debug(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, ASTIdentifier | ASTOperator, Any, ASTTemplateArgs], Symbol | None,
|
||
|
],
|
||
|
strictTemplateParamArgLists: bool, ancestorLookupType: str,
|
||
|
templateShorthand: bool, matchSelf: bool,
|
||
|
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:")
|
||
|
logger.debug(self.to_string(Symbol.debug_indent + 1), end="")
|
||
|
Symbol.debug_print("nestedName: ", nestedName)
|
||
|
Symbol.debug_print("templateDecls: ", ",".join(str(t) for t in 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.
|
||
|
# But to declare a template there must be an additional template parameter list.
|
||
|
assert (nestedName.num_templates() == len(templateDecls) or
|
||
|
nestedName.num_templates() + 1 == len(templateDecls))
|
||
|
else:
|
||
|
assert len(templateDecls) <= nestedName.num_templates() + 1
|
||
|
|
||
|
names = nestedName.names
|
||
|
|
||
|
# find the right starting point for lookup
|
||
|
parentSymbol = self
|
||
|
if nestedName.rooted:
|
||
|
while parentSymbol.parent:
|
||
|
parentSymbol = parentSymbol.parent
|
||
|
if ancestorLookupType is not None:
|
||
|
# walk up until we find the first identifier
|
||
|
firstName = names[0]
|
||
|
if not firstName.is_operator():
|
||
|
while parentSymbol.parent:
|
||
|
if parentSymbol.find_identifier(firstName.identOrOp,
|
||
|
matchSelf=matchSelf,
|
||
|
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
|
||
|
parentSymbol.parent and
|
||
|
parentSymbol.parent.identOrOp == firstName.identOrOp):
|
||
|
pass
|
||
|
else:
|
||
|
break
|
||
|
parentSymbol = parentSymbol.parent
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("starting point:")
|
||
|
logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
|
||
|
|
||
|
# and now the actual lookup
|
||
|
iTemplateDecl = 0
|
||
|
for name in names[:-1]:
|
||
|
identOrOp = name.identOrOp
|
||
|
templateArgs = name.templateArgs
|
||
|
if strictTemplateParamArgLists:
|
||
|
# there must be a parameter list
|
||
|
if templateArgs:
|
||
|
assert iTemplateDecl < len(templateDecls)
|
||
|
templateParams = templateDecls[iTemplateDecl]
|
||
|
iTemplateDecl += 1
|
||
|
else:
|
||
|
templateParams = None
|
||
|
else:
|
||
|
# take the next template parameter list if there is one
|
||
|
# otherwise it's ok
|
||
|
if templateArgs and iTemplateDecl < len(templateDecls):
|
||
|
templateParams = templateDecls[iTemplateDecl]
|
||
|
iTemplateDecl += 1
|
||
|
else:
|
||
|
templateParams = None
|
||
|
|
||
|
symbol = parentSymbol._find_first_named_symbol(
|
||
|
identOrOp,
|
||
|
templateParams, templateArgs,
|
||
|
templateShorthand=templateShorthand,
|
||
|
matchSelf=matchSelf,
|
||
|
recurseInAnon=recurseInAnon,
|
||
|
correctPrimaryTemplateArgs=correctPrimaryTemplateArgs)
|
||
|
if symbol is None:
|
||
|
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
|
||
|
# even more. (see also issue #2666)
|
||
|
matchSelf = False
|
||
|
parentSymbol = symbol
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("handle last name from:")
|
||
|
logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
|
||
|
|
||
|
# handle the last name
|
||
|
name = names[-1]
|
||
|
identOrOp = name.identOrOp
|
||
|
templateArgs = name.templateArgs
|
||
|
if iTemplateDecl < len(templateDecls):
|
||
|
assert iTemplateDecl + 1 == len(templateDecls)
|
||
|
templateParams = templateDecls[iTemplateDecl]
|
||
|
else:
|
||
|
assert iTemplateDecl == len(templateDecls)
|
||
|
templateParams = None
|
||
|
|
||
|
symbols = parentSymbol._find_named_symbols(
|
||
|
identOrOp, templateParams, templateArgs,
|
||
|
templateShorthand=templateShorthand, matchSelf=matchSelf,
|
||
|
recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False,
|
||
|
searchInSiblings=searchInSiblings)
|
||
|
if Symbol.debug_lookup:
|
||
|
symbols = list(symbols) # type: ignore[assignment]
|
||
|
Symbol.debug_indent -= 2
|
||
|
return SymbolLookupResult(symbols, parentSymbol,
|
||
|
identOrOp, templateParams, templateArgs)
|
||
|
|
||
|
def _add_symbols(
|
||
|
self,
|
||
|
nestedName: ASTNestedName,
|
||
|
templateDecls: list[Any],
|
||
|
declaration: ASTDeclaration | None,
|
||
|
docname: str | None,
|
||
|
line: int | None,
|
||
|
) -> Symbol:
|
||
|
# Used for adding a whole path of symbols, where the last may or may not
|
||
|
# be an actual declaration.
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent += 1
|
||
|
Symbol.debug_print("_add_symbols:")
|
||
|
Symbol.debug_indent += 1
|
||
|
Symbol.debug_print("tdecls:", ",".join(str(t) for t in templateDecls))
|
||
|
Symbol.debug_print("nn: ", nestedName)
|
||
|
Symbol.debug_print("decl: ", declaration)
|
||
|
Symbol.debug_print(f"location: {docname}:{line}")
|
||
|
|
||
|
def onMissingQualifiedSymbol(parentSymbol: Symbol,
|
||
|
identOrOp: ASTIdentifier | ASTOperator,
|
||
|
templateParams: Any, templateArgs: ASTTemplateArgs,
|
||
|
) -> Symbol | None:
|
||
|
if Symbol.debug_lookup:
|
||
|
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,
|
||
|
docname=None, line=None)
|
||
|
|
||
|
lookupResult = self._symbol_lookup(nestedName, templateDecls,
|
||
|
onMissingQualifiedSymbol,
|
||
|
strictTemplateParamArgLists=True,
|
||
|
ancestorLookupType=None,
|
||
|
templateShorthand=False,
|
||
|
matchSelf=False,
|
||
|
recurseInAnon=False,
|
||
|
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:
|
||
|
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(f"location: {docname}:{line}")
|
||
|
Symbol.debug_indent -= 1
|
||
|
symbol = Symbol(parent=lookupResult.parentSymbol,
|
||
|
identOrOp=lookupResult.identOrOp,
|
||
|
templateParams=lookupResult.templateParams,
|
||
|
templateArgs=lookupResult.templateArgs,
|
||
|
declaration=declaration,
|
||
|
docname=docname, line=line)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 2
|
||
|
return symbol
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
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:
|
||
|
Symbol.debug_print("no declaration")
|
||
|
Symbol.debug_indent -= 2
|
||
|
# good, just a scope creation
|
||
|
# TODO: what if we have more than one symbol?
|
||
|
return symbols[0]
|
||
|
|
||
|
noDecl = []
|
||
|
withDecl = []
|
||
|
dupDecl = []
|
||
|
for s in symbols:
|
||
|
if s.declaration is None:
|
||
|
noDecl.append(s)
|
||
|
elif s.isRedeclaration:
|
||
|
dupDecl.append(s)
|
||
|
else:
|
||
|
withDecl.append(s)
|
||
|
if Symbol.debug_lookup:
|
||
|
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
|
||
|
# deleted, and other duplicates exist? The full document should probably be rebuild.
|
||
|
|
||
|
# 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() -> Symbol:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("begin: creating candidate symbol")
|
||
|
symbol = Symbol(parent=lookupResult.parentSymbol,
|
||
|
identOrOp=lookupResult.identOrOp,
|
||
|
templateParams=lookupResult.templateParams,
|
||
|
templateArgs=lookupResult.templateArgs,
|
||
|
declaration=declaration,
|
||
|
docname=docname, line=line)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("end: creating candidate symbol")
|
||
|
return symbol
|
||
|
if len(withDecl) == 0:
|
||
|
candSymbol = None
|
||
|
else:
|
||
|
candSymbol = makeCandSymbol()
|
||
|
|
||
|
def handleDuplicateDeclaration(symbol: Symbol, candSymbol: Symbol) -> None:
|
||
|
if Symbol.debug_lookup:
|
||
|
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.
|
||
|
# 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:
|
||
|
Symbol.debug_print("candId:", candId)
|
||
|
for symbol in withDecl:
|
||
|
# but all existing must be functions as well,
|
||
|
# otherwise we declare it to be a duplicate
|
||
|
if symbol.declaration.objectType != 'function':
|
||
|
handleDuplicateDeclaration(symbol, candSymbol)
|
||
|
# (not reachable)
|
||
|
oldId = symbol.declaration.get_newest_id()
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_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:
|
||
|
Symbol.debug_print("no match, no empty")
|
||
|
if candSymbol is not None:
|
||
|
Symbol.debug_print("result is already created candSymbol")
|
||
|
else:
|
||
|
Symbol.debug_print("result is makeCandSymbol()")
|
||
|
Symbol.debug_indent -= 2
|
||
|
if candSymbol is not None:
|
||
|
return candSymbol
|
||
|
else:
|
||
|
return makeCandSymbol()
|
||
|
else:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print(
|
||
|
"no match, but fill an empty declaration, candSybmol is not None?:",
|
||
|
candSymbol is not None,
|
||
|
)
|
||
|
Symbol.debug_indent -= 2
|
||
|
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, line)
|
||
|
return 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
|
||
|
|
||
|
def unconditionalAdd(self: Symbol, otherChild: Symbol) -> None:
|
||
|
# TODO: hmm, should we prune by docnames?
|
||
|
self._children.append(otherChild)
|
||
|
otherChild.parent = self
|
||
|
otherChild._assert_invariants()
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent += 1
|
||
|
for otherChild in other._children:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("otherChild:\n", otherChild.to_string(Symbol.debug_indent))
|
||
|
Symbol.debug_indent += 1
|
||
|
if otherChild.isRedeclaration:
|
||
|
unconditionalAdd(self, otherChild)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("isRedeclaration")
|
||
|
Symbol.debug_indent -= 1
|
||
|
continue
|
||
|
candiateIter = self._find_named_symbols(
|
||
|
identOrOp=otherChild.identOrOp,
|
||
|
templateParams=otherChild.templateParams,
|
||
|
templateArgs=otherChild.templateArgs,
|
||
|
templateShorthand=False, matchSelf=False,
|
||
|
recurseInAnon=False, correctPrimaryTemplateArgs=False,
|
||
|
searchInSiblings=False)
|
||
|
candidates = list(candiateIter)
|
||
|
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("raw candidate symbols:", len(candidates))
|
||
|
symbols = [s for s in candidates if not s.isRedeclaration]
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("non-duplicate candidate symbols:", len(symbols))
|
||
|
|
||
|
if len(symbols) == 0:
|
||
|
unconditionalAdd(self, otherChild)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
continue
|
||
|
|
||
|
ourChild = None
|
||
|
if otherChild.declaration is None:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("no declaration in other child")
|
||
|
ourChild = symbols[0]
|
||
|
else:
|
||
|
queryId = otherChild.declaration.get_newest_id()
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("queryId: ", queryId)
|
||
|
for symbol in symbols:
|
||
|
if symbol.declaration is None:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("empty candidate")
|
||
|
# if in the end we have non-matching, but have an empty one,
|
||
|
# then just continue with that
|
||
|
ourChild = symbol
|
||
|
continue
|
||
|
candId = symbol.declaration.get_newest_id()
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_print("candidate:", candId)
|
||
|
if candId == queryId:
|
||
|
ourChild = symbol
|
||
|
break
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
if ourChild is None:
|
||
|
unconditionalAdd(self, otherChild)
|
||
|
continue
|
||
|
if otherChild.declaration and otherChild.docname in docnames:
|
||
|
if not ourChild.declaration:
|
||
|
ourChild._fill_empty(otherChild.declaration,
|
||
|
otherChild.docname, otherChild.line)
|
||
|
elif ourChild.docname != otherChild.docname:
|
||
|
name = str(ourChild.declaration)
|
||
|
msg = __("Duplicate C++ declaration, also defined at %s:%s.\n"
|
||
|
"Declaration is '.. cpp:%s:: %s'.")
|
||
|
msg = msg % (ourChild.docname, ourChild.line,
|
||
|
ourChild.declaration.directiveType, name)
|
||
|
logger.warning(msg, location=(otherChild.docname, otherChild.line))
|
||
|
else:
|
||
|
if (otherChild.declaration.objectType ==
|
||
|
ourChild.declaration.objectType and
|
||
|
otherChild.declaration.objectType in
|
||
|
('templateParam', 'functionParam') and
|
||
|
ourChild.parent.declaration == otherChild.parent.declaration):
|
||
|
# `ourChild` was just created during merging by the call
|
||
|
# to `_fill_empty` on the parent and can be ignored.
|
||
|
pass
|
||
|
else:
|
||
|
# Both have declarations, and in the same docname.
|
||
|
# This can apparently happen, it should be safe to
|
||
|
# just ignore it, right?
|
||
|
# Hmm, only on duplicate declarations, right?
|
||
|
msg = "Internal C++ domain error during symbol merging.\n"
|
||
|
msg += "ourChild:\n" + ourChild.to_string(1)
|
||
|
msg += "\notherChild:\n" + otherChild.to_string(1)
|
||
|
logger.warning(msg, location=otherChild.docname)
|
||
|
ourChild.merge_with(otherChild, docnames, env)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 2
|
||
|
|
||
|
def add_name(self, nestedName: ASTNestedName,
|
||
|
templatePrefix: ASTTemplateDeclarationPrefix | None = None) -> Symbol:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent += 1
|
||
|
Symbol.debug_print("add_name:")
|
||
|
if templatePrefix:
|
||
|
templateDecls = templatePrefix.templates
|
||
|
else:
|
||
|
templateDecls = []
|
||
|
res = self._add_symbols(nestedName, templateDecls,
|
||
|
declaration=None, docname=None, line=None)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
return res
|
||
|
|
||
|
def add_declaration(self, declaration: ASTDeclaration,
|
||
|
docname: str, line: int) -> Symbol:
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent += 1
|
||
|
Symbol.debug_print("add_declaration:")
|
||
|
assert declaration is not None
|
||
|
assert docname is not None
|
||
|
assert line is not None
|
||
|
nestedName = declaration.name
|
||
|
if declaration.templatePrefix:
|
||
|
templateDecls = declaration.templatePrefix.templates
|
||
|
else:
|
||
|
templateDecls = []
|
||
|
res = self._add_symbols(nestedName, templateDecls, declaration, docname, line)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
return res
|
||
|
|
||
|
def find_identifier(self, identOrOp: ASTIdentifier | ASTOperator,
|
||
|
matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool,
|
||
|
) -> Symbol | None:
|
||
|
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)
|
||
|
logger.debug(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:")
|
||
|
logger.debug(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: 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:
|
||
|
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:
|
||
|
logger.debug(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,
|
||
|
searchInSiblings: bool,
|
||
|
) -> tuple[list[Symbol] | None, 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:")
|
||
|
logger.debug(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: ASTIdentifier | ASTOperator,
|
||
|
templateParams: Any,
|
||
|
templateArgs: ASTTemplateArgs) -> Symbol | None:
|
||
|
# TODO: Maybe search without template args?
|
||
|
# 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
|
||
|
|
||
|
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
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 2
|
||
|
return None, None
|
||
|
|
||
|
res = list(lookupResult.symbols)
|
||
|
if len(res) != 0:
|
||
|
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], None
|
||
|
else:
|
||
|
return None, None
|
||
|
|
||
|
def find_declaration(self, declaration: ASTDeclaration, typ: str, templateShorthand: bool,
|
||
|
matchSelf: bool, recurseInAnon: bool) -> Symbol | None:
|
||
|
# 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
|
||
|
else:
|
||
|
templateDecls = []
|
||
|
|
||
|
def onMissingQualifiedSymbol(parentSymbol: Symbol,
|
||
|
identOrOp: ASTIdentifier | ASTOperator,
|
||
|
templateParams: Any,
|
||
|
templateArgs: ASTTemplateArgs) -> Symbol | None:
|
||
|
return None
|
||
|
|
||
|
lookupResult = self._symbol_lookup(nestedName, templateDecls,
|
||
|
onMissingQualifiedSymbol,
|
||
|
strictTemplateParamArgLists=False,
|
||
|
ancestorLookupType=typ,
|
||
|
templateShorthand=templateShorthand,
|
||
|
matchSelf=matchSelf,
|
||
|
recurseInAnon=recurseInAnon,
|
||
|
correctPrimaryTemplateArgs=False,
|
||
|
searchInSiblings=False)
|
||
|
if Symbol.debug_lookup:
|
||
|
Symbol.debug_indent -= 1
|
||
|
if lookupResult is None:
|
||
|
return None
|
||
|
|
||
|
symbols = list(lookupResult.symbols)
|
||
|
if len(symbols) == 0:
|
||
|
return None
|
||
|
|
||
|
querySymbol = Symbol(parent=lookupResult.parentSymbol,
|
||
|
identOrOp=lookupResult.identOrOp,
|
||
|
templateParams=lookupResult.templateParams,
|
||
|
templateArgs=lookupResult.templateArgs,
|
||
|
declaration=declaration,
|
||
|
docname='fakeDocnameForQuery',
|
||
|
line=42)
|
||
|
queryId = declaration.get_newest_id()
|
||
|
for symbol in symbols:
|
||
|
if symbol.declaration is None:
|
||
|
continue
|
||
|
candId = symbol.declaration.get_newest_id()
|
||
|
if candId == queryId:
|
||
|
querySymbol.remove()
|
||
|
return symbol
|
||
|
querySymbol.remove()
|
||
|
return None
|
||
|
|
||
|
def to_string(self, indent: int) -> str:
|
||
|
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(Symbol.debug_indent_string * indent)
|
||
|
if self.identOrOp:
|
||
|
res.append(str(self.identOrOp))
|
||
|
else:
|
||
|
res.append(str(self.declaration))
|
||
|
if self.templateArgs:
|
||
|
res.append(str(self.templateArgs))
|
||
|
if self.declaration:
|
||
|
res.append(": ")
|
||
|
if self.isRedeclaration:
|
||
|
res.append('!!duplicate!! ')
|
||
|
res.append("{" + self.declaration.objectType + "} ")
|
||
|
res.append(str(self.declaration))
|
||
|
if self.docname:
|
||
|
res.append('\t(')
|
||
|
res.append(self.docname)
|
||
|
res.append(')')
|
||
|
res.append('\n')
|
||
|
return ''.join(res)
|
||
|
|
||
|
def dump(self, indent: int) -> str:
|
||
|
return ''.join([
|
||
|
self.to_string(indent),
|
||
|
*(c.dump(indent + 1) for c in self._children),
|
||
|
])
|