Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion cxxheaderparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,9 @@ def _parse_template_instantiation(

tok = self.lex.token_if("class", "struct")
if not tok:
raise self._parse_error(tok)
tok = self.lex.token()
self._parse_declarations(tok, doxygen, TemplateDecl(), allow_fields=False)
return

atok = self.lex.token_if_in_set(self._attribute_start_tokens)
if atok:
Expand Down Expand Up @@ -2754,6 +2756,7 @@ def _parse_decl(
is_typedef: bool,
is_friend: bool,
attributes: typing.List[Attribute],
allow_fields: bool = True,
) -> bool:
toks: LexTokenList = []

Expand Down Expand Up @@ -2894,6 +2897,9 @@ def _parse_decl(
if not dtype:
raise CxxParseError("appear to be parsing a field without a type")

if not allow_fields:
raise self._parse_error(None)

if isinstance(template, list):
raise CxxParseError("multiple template declarations on a field")

Expand Down Expand Up @@ -2975,6 +2981,7 @@ def _parse_declarations(
template: TemplateDeclTypeVar = None,
is_typedef: bool = False,
is_friend: bool = False,
allow_fields: bool = True,
) -> None:
"""
Parses a sequence of declarations
Expand Down Expand Up @@ -3076,6 +3083,7 @@ def _parse_declarations(
is_typedef,
is_friend,
attributes,
allow_fields,
):
# if it returns True then it handled the end of the statement
break
Expand Down
79 changes: 79 additions & 0 deletions tests/test_template.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Note: testcases generated via `python -m cxxheaderparser.gentest`

import pytest

from cxxheaderparser.errors import CxxParseError
from cxxheaderparser.types import (
Array,
BaseClass,
Expand Down Expand Up @@ -1816,6 +1819,82 @@ class C {
)


def test_template_function_instantiation() -> None:
content = """
template <typename T> T tFunction(T val);
template <typename T> T tFunction(T val) { return val; }
template bool tFunction(bool val);
"""
data = parse_string(content, cleandoc=True)

assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="T")])
),
name=PQName(segments=[NameSpecifier(name="tFunction")]),
parameters=[
Parameter(
type=Type(
typename=PQName(segments=[NameSpecifier(name="T")])
),
name="val",
)
],
template=TemplateDecl(
params=[TemplateTypeParam(typekey="typename", name="T")]
),
),
Function(
return_type=Type(
typename=PQName(segments=[NameSpecifier(name="T")])
),
name=PQName(segments=[NameSpecifier(name="tFunction")]),
parameters=[
Parameter(
type=Type(
typename=PQName(segments=[NameSpecifier(name="T")])
),
name="val",
)
],
has_body=True,
template=TemplateDecl(
params=[TemplateTypeParam(typekey="typename", name="T")]
),
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="bool")])
),
name=PQName(segments=[NameSpecifier(name="tFunction")]),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="bool")]
)
),
name="val",
)
],
template=TemplateDecl(),
),
]
)
)


def test_template_rejects_variable_instantiation() -> None:
with pytest.raises(CxxParseError):
parse_string("template int x;")

with pytest.raises(CxxParseError):
parse_string("template int (x);")


def test_template_instantiation() -> None:
content = """
template class MyClass<1,2>;
Expand Down
Loading