C++, fix for template argument parsing.

Non-type template arguments where not parsed correctly. It is a bit
better now. The example in michaeljones/breathe#218 should work now.
This commit is contained in:
Jakob Lykke Andersen
2015-09-06 20:34:01 +02:00
parent 38db131ee9
commit c9470fac65
2 changed files with 73 additions and 42 deletions

View File

@@ -2268,10 +2268,7 @@ class Symbol(object):
def find_name(self, nestedName, templateDecls, specific_specialisation):
# TODO: unify this with the _add_symbols
# This condition should be checked at the parser level.
# 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))
assert len(templateDecls) <= nestedName.num_templates() + 1
parentSymbol = self
if nestedName.rooted:
while parentSymbol.parent:
@@ -2319,8 +2316,7 @@ class Symbol(object):
assert not name.is_operator()
identifier = name.identifier
templateArgs = name.templateArgs
if templateArgs:
assert iTemplateDecl < len(templateDecls)
if templateArgs and iTemplateDecl < len(templateDecls):
templateParams = templateDecls[iTemplateDecl]
iTemplateDecl += 1
else:
@@ -2386,7 +2382,6 @@ class DefinitionParser(object):
self.warnEnv.warn(msg)
else:
print("Warning: %s" % msg)
print("In declaration:\n%s" % self.definition)
def match(self, regex):
match = regex.match(self.definition, self.pos)
@@ -2496,6 +2491,50 @@ class DefinitionParser(object):
type = self._parse_type(named=False, outer="operatorCast")
return ASTOperatorType(type)
def _parse_template_argument_list(self):
self.skip_ws()
if not self.skip_string('<'):
return None
templateArgs = []
while 1:
pos = self.pos
parsedComma = False
parsedEnd = False
try:
type = self._parse_type(named=False)
self.skip_ws()
if self.skip_string('>'):
parsedEnd = True
elif self.skip_string(','):
parsedComma = True
else:
self.fail('Expected ">" or "," in template argument list.')
templateArgs.append(type)
except DefinitionError as e:
errorType = e.description
self.pos = pos
try:
value = self._parse_expression(end=[',', '>'])
self.skip_ws()
if self.skip_string('>'):
parsedEnd = True
elif self.skip_string(','):
parsedComma = True
else:
self.fail('Expected ">" or "," in template argument list.')
templateArgs.append(ASTTemplateArgConstant(value))
except DefinitionError as e:
errorExpr = e.description
msg = "Error in parsing template argument list. " \
"Error if type argument:\n%s\n" \
"Error if non-type argument:\n%s" \
% (errorType, errorExpr)
self.fail(msg)
if parsedEnd:
assert not parsedComma
break
return ASTTemplateArgs(templateArgs)
def _parse_nested_name(self):
names = []
@@ -2518,31 +2557,7 @@ class DefinitionParser(object):
if identifier in _keywords:
self.fail("Expected identifier in nested name, "
"got keyword: %s" % identifier)
templateArgs = None
self.skip_ws()
if self.skip_string('<'):
templateArgs = []
while 1:
pos = self.pos
try:
type = self._parse_type(named=False)
templateArgs.append(type)
except DefinitionError:
self.pos = pos
try:
value = self._parse_expression(end=[',', '>'])
except DefinitionError:
assert False # TODO: make nice error
templateArgs.append(ASTTemplateArgConstant(value))
self.skip_ws()
if self.skip_string('>'):
break
elif self.skip_string(','):
continue
else:
self.fail('Expected ">" or "," in template '
'argument list.')
templateArgs = ASTTemplateArgs(templateArgs)
templateArgs = self._parse_template_argument_list()
identifier = ASTIdentifier(identifier)
names.append(ASTNestedNameElement(identifier, templateArgs))
@@ -3102,10 +3117,16 @@ class DefinitionParser(object):
% (numArgs, numParams))
if numArgs > numParams:
numExtra = numArgs - numParams
self.warn("Too many template argument lists compared to parameter"
" lists. Argument lists: %d, Parameter lists: %d,"
" Extra empty parameters lists prepended: %d."
% (numArgs, numParams, numExtra))
msg = "Too many template argument lists compared to parameter" \
" lists. Argument lists: %d, Parameter lists: %d," \
" Extra empty parameters lists prepended: %d." \
% (numArgs, numParams, numExtra)
msg += " Declaration:\n\t"
if templatePrefix:
msg += "%s\n\t" % text_type(templatePrefix)
msg += text_type(nestedName)
self.warn(msg)
newTemplates = []
for i in range(numExtra):
newTemplates.append(ASTTemplateParams([]))
@@ -3175,7 +3196,6 @@ class DefinitionParser(object):
def parse_xref_object(self):
templatePrefix = self._parse_template_declaration_prefix()
name = self._parse_nested_name()
templatePrefix = self._check_template_consistency(name, templatePrefix)
res = ASTNamespace(name, templatePrefix)
res.objectType = 'xref'
return res
@@ -3527,16 +3547,20 @@ class CPPDomain(Domain):
# # TODO: merge rootSymbol
def _resolve_xref_inner(self, env, fromdocname, builder,
target, node, contnode, warn=True):
parser = DefinitionParser(target, env)
target, node, contnode, emitWarnings=True):
class Warner(object):
def warn(self, msg):
if emitWarnings:
env.warn_node(msg, node)
warner = Warner()
parser = DefinitionParser(target, warner)
try:
ast = parser.parse_xref_object()
parser.skip_ws()
if not parser.eof:
raise DefinitionError('')
except DefinitionError:
if warn:
env.warn_node('Unparseable C++ cross-reference: %r' % target, node)
warner.warn('Unparseable C++ cross-reference: %r' % target)
return None, None
parentKey = node.get("cpp:parentKey", None)
rootSymbol = self.data['rootSymbol']
@@ -3580,7 +3604,7 @@ class CPPDomain(Domain):
node, contnode):
node, objtype = self._resolve_xref_inner(env, fromdocname, builder,
target, node, contnode,
warn=False)
emitWarnings=False)
if node:
return [('cpp:' + self.role_for_objtype(objtype), node)]
return []

View File

@@ -40,6 +40,7 @@ def check(name, input, idv1output=None, idv2output=None, output=None):
ast = parse(name, input)
res = text_type(ast)
if res != output:
print("")
print("Input: ", text_type(input))
print("Result: ", res)
print("Expected: ", output)
@@ -263,6 +264,12 @@ def test_templates():
check('class', "template<int T = 42> A", None, "I_iE1A")
check('class', "template<int = 42> A", None, "I_iE1A")
# from breathe#218
check('function',
"template<typename F> "
"void allow(F *f, typename func<F, B, G!=1>::type tt)",
None, "I0E5allowP1FN4funcI1F1BXG!=1EE4typeE")
def test_bases():
check('class', 'A', "A", "1A")