C++, fix handling of (templated) operators

This commit is contained in:
Jakob Lykke Andersen 2017-12-28 16:50:32 +01:00
parent 324bc7e3c4
commit f3c05adebc
3 changed files with 77 additions and 117 deletions

View File

@ -1408,9 +1408,9 @@ class ASTTemplateParamNonType(ASTBase):
name = self.param.name name = self.param.name
if name: if name:
assert len(name.names) == 1 assert len(name.names) == 1
assert name.names[0].identifier assert name.names[0].identOrOp
assert not name.names[0].templateArgs assert not name.names[0].templateArgs
return name.names[0].identifier return name.names[0].identOrOp
else: else:
return None return None
@ -1650,8 +1650,8 @@ class ASTOperatorBuildIn(ASTBase):
else: else:
return u'operator' + self.op return u'operator' + self.op
def describe_signature(self, signode, mode, env, prefix, symbol): def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None # type: (addnodes.desc_signature, unicode, Any, unicode, unicode, Symbol) -> None
_verify_description_mode(mode) _verify_description_mode(mode)
identifier = text_type(self) identifier = text_type(self)
if mode == 'lastIsName': if mode == 'lastIsName':
@ -1684,7 +1684,7 @@ class ASTOperatorType(ASTBase):
# type: () -> unicode # type: () -> unicode
return text_type(self) return text_type(self)
def describe_signature(self, signode, mode, env, prefix, symbol): def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
_verify_description_mode(mode) _verify_description_mode(mode)
identifier = text_type(self) identifier = text_type(self)
@ -1714,7 +1714,7 @@ class ASTOperatorLiteral(ASTBase):
# type: () -> unicode # type: () -> unicode
return u'operator""' + text_type(self.identifier) return u'operator""' + text_type(self.identifier)
def describe_signature(self, signode, mode, env, prefix, symbol): def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
_verify_description_mode(mode) _verify_description_mode(mode)
identifier = text_type(self) identifier = text_type(self)
@ -1788,9 +1788,9 @@ class ASTTemplateArgs(ASTBase):
class ASTNestedNameElement(ASTBase): class ASTNestedNameElement(ASTBase):
def __init__(self, identifier, templateArgs): def __init__(self, identOrOp, templateArgs):
# type: (Any, Any) -> None # type: (Any, Any) -> None
self.identifier = identifier self.identOrOp = identOrOp
self.templateArgs = templateArgs self.templateArgs = templateArgs
def is_operator(self): def is_operator(self):
@ -1799,14 +1799,14 @@ class ASTNestedNameElement(ASTBase):
def get_id(self, version): def get_id(self, version):
# type: (int) -> unicode # type: (int) -> unicode
res = self.identifier.get_id(version) res = self.identOrOp.get_id(version)
if self.templateArgs: if self.templateArgs:
res += self.templateArgs.get_id(version) res += self.templateArgs.get_id(version)
return res return res
def __unicode__(self): def __unicode__(self):
# type: () -> unicode # type: () -> unicode
res = text_type(self.identifier) res = text_type(self.identOrOp)
if self.templateArgs: if self.templateArgs:
res += text_type(self.templateArgs) res += text_type(self.templateArgs)
return res return res
@ -1814,7 +1814,7 @@ class ASTNestedNameElement(ASTBase):
def describe_signature(self, signode, mode, env, prefix, symbol): def describe_signature(self, signode, mode, env, prefix, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None # type: (addnodes.desc_signature, unicode, BuildEnvironment, unicode, Symbol) -> None
tArgs = text_type(self.templateArgs) if self.templateArgs is not None else '' tArgs = text_type(self.templateArgs) if self.templateArgs is not None else ''
self.identifier.describe_signature(signode, mode, env, prefix, tArgs, symbol) self.identOrOp.describe_signature(signode, mode, env, prefix, tArgs, symbol)
if self.templateArgs is not None: if self.templateArgs is not None:
self.templateArgs.describe_signature(signode, mode, env, symbol) self.templateArgs.describe_signature(signode, mode, env, symbol)
@ -3310,23 +3310,20 @@ class Symbol(object):
# type: () -> None # type: () -> None
if not self.parent: if not self.parent:
# parent == None means global scope, so declaration means a parent # parent == None means global scope, so declaration means a parent
assert not self.identifier assert not self.identOrOp
assert not self.templateParams assert not self.templateParams
assert not self.templateArgs assert not self.templateArgs
assert not self.declaration assert not self.declaration
assert not self.docname assert not self.docname
else: else:
if not self.identifier:
# in case it's an operator
assert self.declaration
if self.declaration: if self.declaration:
assert self.docname assert self.docname
def __init__(self, parent, identifier, def __init__(self, parent, identOrOp,
templateParams, templateArgs, declaration, docname): templateParams, templateArgs, declaration, docname):
# type: (Any, Any, Any, Any, Any, unicode) -> None # type: (Any, Any, Any, Any, Any, unicode) -> None
self.parent = parent self.parent = parent
self.identifier = identifier self.identOrOp = identOrOp
self.templateParams = templateParams # template<templateParams> self.templateParams = templateParams # template<templateParams>
self.templateArgs = templateArgs # identifier<templateArgs> self.templateArgs = templateArgs # identifier<templateArgs>
self.declaration = declaration self.declaration = declaration
@ -3365,8 +3362,8 @@ class Symbol(object):
decl = ASTDeclaration('functionParam', None, None, p) decl = ASTDeclaration('functionParam', None, None, p)
assert not nn.rooted assert not nn.rooted
assert len(nn.names) == 1 assert len(nn.names) == 1
identifier = nn.names[0].identifier identOrOp = nn.names[0].identOrOp
Symbol(parent=self, identifier=identifier, Symbol(parent=self, identOrOp=identOrOp,
templateParams=None, templateArgs=None, templateParams=None, templateArgs=None,
declaration=decl, docname=docname) declaration=decl, docname=docname)
@ -3390,12 +3387,7 @@ class Symbol(object):
if sChild.declaration and sChild.docname == docname: if sChild.declaration and sChild.docname == docname:
sChild.declaration = None sChild.declaration = None
sChild.docname = None sChild.docname = None
# Just remove operators, because there is no identification if newChildren.append(sChild)
# they got removed.
# Don't remove other symbols because they may be used in namespace
# directives.
if sChild.identifier or sChild.declaration:
newChildren.append(sChild)
self.children = newChildren self.children = newChildren
def get_all_symbols(self): def get_all_symbols(self):
@ -3415,11 +3407,7 @@ class Symbol(object):
symbols.reverse() symbols.reverse()
key = [] key = []
for s in symbols: for s in symbols:
if s.identifier: nne = ASTNestedNameElement(s.identOrOp, s.templateArgs)
nne = ASTNestedNameElement(s.identifier, s.templateArgs)
else:
assert s.declaration
nne = s.declaration.name.names[-1]
key.append((nne, s.templateParams)) key.append((nne, s.templateParams))
return key return key
@ -3432,11 +3420,9 @@ class Symbol(object):
templates.append(False) templates.append(False)
return ASTNestedName(names, templates, rooted=False) return ASTNestedName(names, templates, rooted=False)
def _find_named_symbol(self, identifier, templateParams, def _find_named_symbol(self, identOrOp, templateParams, templateArgs,
templateArgs, operator,
templateShorthand, matchSelf): templateShorthand, matchSelf):
# type: (Any, Any, Any, Any, Any, bool) -> Symbol # type: (Any, Any, Any, Any, bool) -> Symbol
assert (identifier is None) != (operator is None)
def isSpecialization(): def isSpecialization():
# the names of the template parameters must be given exactly as args # the names of the template parameters must be given exactly as args
@ -3464,17 +3450,8 @@ class Symbol(object):
templateArgs = None templateArgs = None
def matches(s): def matches(s):
if s.identifier != identifier: if s.identOrOp != identOrOp:
return False return False
if not s.identifier:
if not s.declaration:
return False
assert operator
name = s.declaration.name.names[-1]
if not name.is_operator():
return False
if text_type(name) != text_type(operator):
return False
if (s.templateParams is None) != (templateParams is None): if (s.templateParams is None) != (templateParams is None):
if templateParams is not None: if templateParams is not None:
# we query with params, they must match params # we query with params, they must match params
@ -3515,10 +3492,7 @@ class Symbol(object):
names = nestedName.names names = nestedName.names
iTemplateDecl = 0 iTemplateDecl = 0
for name in names[:-1]: for name in names[:-1]:
# there shouldn't be anything inside an operator identOrOp = name.identOrOp
# (other than template parameters, which are not added this way, right?)
assert not name.is_operator()
identifier = name.identifier
templateArgs = name.templateArgs templateArgs = name.templateArgs
if templateArgs: if templateArgs:
assert iTemplateDecl < len(templateDecls) assert iTemplateDecl < len(templateDecls)
@ -3526,27 +3500,20 @@ class Symbol(object):
iTemplateDecl += 1 iTemplateDecl += 1
else: else:
templateParams = None templateParams = None
symbol = parentSymbol._find_named_symbol(identifier, symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateParams,
templateArgs, templateArgs,
operator=None,
templateShorthand=False, templateShorthand=False,
matchSelf=False) matchSelf=False)
if symbol is None: if symbol is None:
symbol = Symbol(parent=parentSymbol, identifier=identifier, symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams, templateParams=templateParams,
templateArgs=templateArgs, declaration=None, templateArgs=templateArgs, declaration=None,
docname=None) docname=None)
parentSymbol = symbol parentSymbol = symbol
name = names[-1] name = names[-1]
if name.is_operator(): identOrOp = name.identOrOp
identifier = None templateArgs = name.templateArgs
templateArgs = None
operator = name
else:
identifier = name.identifier
templateArgs = name.templateArgs
operator = None
if iTemplateDecl < len(templateDecls): if iTemplateDecl < len(templateDecls):
if iTemplateDecl + 1 != len(templateDecls): if iTemplateDecl + 1 != len(templateDecls):
print(text_type(templateDecls)) print(text_type(templateDecls))
@ -3556,10 +3523,9 @@ class Symbol(object):
else: else:
assert iTemplateDecl == len(templateDecls) assert iTemplateDecl == len(templateDecls)
templateParams = None templateParams = None
symbol = parentSymbol._find_named_symbol(identifier, symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateParams,
templateArgs, templateArgs,
operator,
templateShorthand=False, templateShorthand=False,
matchSelf=False) matchSelf=False)
if symbol: if symbol:
@ -3576,7 +3542,7 @@ class Symbol(object):
return symbol return symbol
# It may simply be a function overload, so let's compare ids. # It may simply be a function overload, so let's compare ids.
isRedeclaration = True isRedeclaration = True
candSymbol = Symbol(parent=parentSymbol, identifier=identifier, candSymbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams, templateParams=templateParams,
templateArgs=templateArgs, templateArgs=templateArgs,
declaration=declaration, declaration=declaration,
@ -3596,7 +3562,7 @@ class Symbol(object):
candSymbol.isRedeclaration = True candSymbol.isRedeclaration = True
raise _DuplicateSymbolError(symbol, declaration) raise _DuplicateSymbolError(symbol, declaration)
else: else:
symbol = Symbol(parent=parentSymbol, identifier=identifier, symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams, templateParams=templateParams,
templateArgs=templateArgs, templateArgs=templateArgs,
declaration=declaration, declaration=declaration,
@ -3667,12 +3633,12 @@ class Symbol(object):
templateDecls = [] templateDecls = []
return self._add_symbols(nestedName, templateDecls, declaration, docname) return self._add_symbols(nestedName, templateDecls, declaration, docname)
def find_identifier(self, identifier, matchSelf): def find_identifier(self, identOrOp, matchSelf):
# type: (Any, bool) -> Symbol # type: (Any, bool) -> Symbol
if matchSelf and self.identifier and self.identifier == identifier: if matchSelf and self.identOrOp == identOrOp:
return self return self
for s in self.children: for s in self.children:
if s.identifier and s.identifier == identifier: if s.identOrOp == identOrOp:
return s return s
return None return None
@ -3680,16 +3646,10 @@ class Symbol(object):
# type: (List[Tuple[Any, Any]]) -> Symbol # type: (List[Tuple[Any, Any]]) -> Symbol
s = self s = self
for name, templateParams in key: for name, templateParams in key:
if name.is_operator(): identOrOp = name.identOrOp
identifier = None templateArgs = name.templateArgs
templateArgs = None s = s._find_named_symbol(identOrOp,
operator = name templateParams, templateArgs,
else:
identifier = name.identifier
templateArgs = name.templateArgs
operator = None
s = s._find_named_symbol(identifier, templateParams,
templateArgs, operator,
templateShorthand=False, templateShorthand=False,
matchSelf=False) matchSelf=False)
if not s: if not s:
@ -3713,13 +3673,13 @@ class Symbol(object):
firstName = names[0] firstName = names[0]
if not firstName.is_operator(): if not firstName.is_operator():
while parentSymbol.parent: while parentSymbol.parent:
if parentSymbol.find_identifier(firstName.identifier, if parentSymbol.find_identifier(firstName.identOrOp,
matchSelf=matchSelf): matchSelf=matchSelf):
# if we are in the scope of a constructor but wants to reference the class # if we are in the scope of a constructor but wants to reference the class
# we need to walk one extra up # we need to walk one extra up
if (len(names) == 1 and typ == 'class' and matchSelf and if (len(names) == 1 and typ == 'class' and matchSelf and
parentSymbol.parent and parentSymbol.parent.identifier and parentSymbol.parent and
parentSymbol.parent.identifier == firstName.identifier): parentSymbol.parent.identOrOp == firstName.identOrOp):
pass pass
else: else:
break break
@ -3728,48 +3688,36 @@ class Symbol(object):
for iName in range(len(names)): for iName in range(len(names)):
name = names[iName] name = names[iName]
if iName + 1 == len(names): if iName + 1 == len(names):
if name.is_operator(): identOrOp = name.identOrOp
identifier = None templateArgs = name.templateArgs
templateArgs = None
operator = name
else:
identifier = name.identifier
templateArgs = name.templateArgs
operator = None
if iTemplateDecl < len(templateDecls): if iTemplateDecl < len(templateDecls):
assert iTemplateDecl + 1 == len(templateDecls) assert iTemplateDecl + 1 == len(templateDecls)
templateParams = templateDecls[iTemplateDecl] templateParams = templateDecls[iTemplateDecl]
else: else:
assert iTemplateDecl == len(templateDecls) assert iTemplateDecl == len(templateDecls)
templateParams = None templateParams = None
symbol = parentSymbol._find_named_symbol(identifier, symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateArgs, templateParams, templateArgs,
operator,
templateShorthand=templateShorthand, templateShorthand=templateShorthand,
matchSelf=matchSelf) matchSelf=matchSelf)
if symbol is not None: if symbol is not None:
return symbol return symbol
# try without template params and args # try without template params and args
symbol = parentSymbol._find_named_symbol(identifier, symbol = parentSymbol._find_named_symbol(identOrOp,
None, None, None, None,
operator,
templateShorthand=templateShorthand, templateShorthand=templateShorthand,
matchSelf=matchSelf) matchSelf=matchSelf)
return symbol return symbol
else: else:
# there shouldn't be anything inside an operator identOrOp = name.identOrOp
assert not name.is_operator()
identifier = name.identifier
templateArgs = name.templateArgs templateArgs = name.templateArgs
if templateArgs and iTemplateDecl < len(templateDecls): if templateArgs and iTemplateDecl < len(templateDecls):
templateParams = templateDecls[iTemplateDecl] templateParams = templateDecls[iTemplateDecl]
iTemplateDecl += 1 iTemplateDecl += 1
else: else:
templateParams = None templateParams = None
symbol = parentSymbol._find_named_symbol(identifier, symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateParams, templateArgs,
templateArgs,
operator=None,
templateShorthand=templateShorthand, templateShorthand=templateShorthand,
matchSelf=matchSelf) matchSelf=matchSelf)
if symbol is None: if symbol is None:
@ -3792,8 +3740,8 @@ class Symbol(object):
res.append(text_type(self.templateParams)) res.append(text_type(self.templateParams))
res.append('\n') res.append('\n')
res.append('\t' * indent) res.append('\t' * indent)
if self.identifier: if self.identOrOp:
res.append(text_type(self.identifier)) res.append(text_type(self.identOrOp))
else: else:
res.append(text_type(self.declaration)) res.append(text_type(self.declaration))
if self.templateArgs: if self.templateArgs:
@ -4561,8 +4509,7 @@ class DefinitionParser(object):
template = False template = False
templates.append(template) templates.append(template)
if self.skip_word_and_ws('operator'): if self.skip_word_and_ws('operator'):
op = self._parse_operator() identOrOp = self._parse_operator()
names.append(op)
else: else:
if not self.match(_identifier_re): if not self.match(_identifier_re):
if memberPointer and len(names) > 0: if memberPointer and len(names) > 0:
@ -4574,17 +4521,17 @@ class DefinitionParser(object):
if identifier in _keywords: if identifier in _keywords:
self.fail("Expected identifier in nested name, " self.fail("Expected identifier in nested name, "
"got keyword: %s" % identifier) "got keyword: %s" % identifier)
# try greedily to get template parameters, identOrOp = ASTIdentifier(identifier) # type: ignore
# but otherwise a < might be because we are in an expression # try greedily to get template arguments,
pos = self.pos # but otherwise a < might be because we are in an expression
try: pos = self.pos
templateArgs = self._parse_template_argument_list() try:
except DefinitionError as ex: templateArgs = self._parse_template_argument_list()
self.pos = pos except DefinitionError as ex:
templateArgs = None self.pos = pos
self.otherErrors.append(ex) templateArgs = None
identifier = ASTIdentifier(identifier) # type: ignore self.otherErrors.append(ex)
names.append(ASTNestedNameElement(identifier, templateArgs)) names.append(ASTNestedNameElement(identOrOp, templateArgs))
self.skip_ws() self.skip_ws()
if not self.skip_string('::'): if not self.skip_string('::'):
@ -5532,7 +5479,7 @@ class CPPObject(ObjectDescription):
# then add the name to the parent scope # then add the name to the parent scope
symbol = ast.symbol symbol = ast.symbol
assert symbol assert symbol
assert symbol.identifier is not None assert symbol.identOrOp is not None
assert symbol.templateParams is None assert symbol.templateParams is None
assert symbol.templateArgs is None assert symbol.templateArgs is None
parentSymbol = symbol.parent parentSymbol = symbol.parent
@ -5545,7 +5492,7 @@ class CPPObject(ObjectDescription):
if parentDecl is None: if parentDecl is None:
# the parent is not explicitly declared # the parent is not explicitly declared
# TODO: we could warn, but it could be a style to just assume # TODO: we could warn, but it could be a style to just assume
# enumerator parnets to be scoped # enumerator parents to be scoped
return return
if parentDecl.objectType != 'enum': if parentDecl.objectType != 'enum':
# TODO: maybe issue a warning, enumerators in non-enums is weird, # TODO: maybe issue a warning, enumerators in non-enums is weird,
@ -5555,13 +5502,13 @@ class CPPObject(ObjectDescription):
return return
targetSymbol = parentSymbol.parent targetSymbol = parentSymbol.parent
s = targetSymbol.find_identifier(symbol.identifier, matchSelf=False) s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False)
if s is not None: if s is not None:
# something is already declared with that name # something is already declared with that name
return return
declClone = symbol.declaration.clone() declClone = symbol.declaration.clone()
declClone.enumeratorScopedSymbol = symbol declClone.enumeratorScopedSymbol = symbol
Symbol(parent=targetSymbol, identifier=symbol.identifier, Symbol(parent=targetSymbol, identOrOp=symbol.identOrOp,
templateParams=None, templateArgs=None, templateParams=None, templateArgs=None,
declaration=declClone, declaration=declClone,
docname=self.env.docname) docname=self.env.docname)

View File

@ -28,14 +28,20 @@ directives
An unscoped enum. An unscoped enum.
.. cpp:enumerator:: A
.. cpp:enum-class:: MyScopedEnum .. cpp:enum-class:: MyScopedEnum
A scoped enum. A scoped enum.
.. cpp:enumerator:: B
.. cpp:enum-struct:: protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type .. cpp:enum-struct:: protected MyScopedVisibilityEnum : std::underlying_type<MySpecificEnum>::type
A scoped enum with non-default visibility, and with a specified underlying type. A scoped enum with non-default visibility, and with a specified underlying type.
.. cpp:enumerator:: B
.. cpp:function:: void paren_1(int, float) .. cpp:function:: void paren_1(int, float)
.. cpp:function:: void paren_2(int, float) .. cpp:function:: void paren_2(int, float)

View File

@ -205,6 +205,9 @@ def test_expressions():
check('class', "template<int T = 42> A", {2: "I_iE1A"}) check('class', "template<int T = 42> A", {2: "I_iE1A"})
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"}) check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
exprCheck('operator()()', 'clclE')
exprCheck('operator()<int>()', 'clclIiEE')
def test_type_definitions(): def test_type_definitions():
check("type", "public bool b", {1: "b", 2: "1b"}, "bool b") check("type", "public bool b", {1: "b", 2: "1b"}, "bool b")
@ -245,6 +248,10 @@ def test_type_definitions():
check('type', 'T = A::template B<int>::template C<double>', {2: '1T'}) check('type', 'T = A::template B<int>::template C<double>', {2: '1T'})
check('type', 'T = Q<A::operator()>', {2: '1T'})
check('type', 'T = Q<A::operator()<int>>', {2: '1T'})
check('type', 'T = Q<A::operator bool>', {2: '1T'})
def test_concept_definitions(): def test_concept_definitions():
check('concept', 'template<typename Param> A::B::Concept', check('concept', 'template<typename Param> A::B::Concept',