Skip to content

Commit 18e24a2

Browse files
authored
Merge pull request #154 from robotpy/fix-102
Fix parsing of explicit instantiations of function templates
2 parents 562b5cc + 66893c4 commit 18e24a2

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

cxxheaderparser/parser.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,9 @@ def _parse_template_instantiation(
795795

796796
tok = self.lex.token_if("class", "struct")
797797
if not tok:
798-
raise self._parse_error(tok)
798+
tok = self.lex.token()
799+
self._parse_declarations(tok, doxygen, TemplateDecl(), allow_fields=False)
800+
return
799801

800802
atok = self.lex.token_if_in_set(self._attribute_start_tokens)
801803
if atok:
@@ -2754,6 +2756,7 @@ def _parse_decl(
27542756
is_typedef: bool,
27552757
is_friend: bool,
27562758
attributes: typing.List[Attribute],
2759+
allow_fields: bool = True,
27572760
) -> bool:
27582761
toks: LexTokenList = []
27592762

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

2900+
if not allow_fields:
2901+
raise self._parse_error(None)
2902+
28972903
if isinstance(template, list):
28982904
raise CxxParseError("multiple template declarations on a field")
28992905

@@ -2975,6 +2981,7 @@ def _parse_declarations(
29752981
template: TemplateDeclTypeVar = None,
29762982
is_typedef: bool = False,
29772983
is_friend: bool = False,
2984+
allow_fields: bool = True,
29782985
) -> None:
29792986
"""
29802987
Parses a sequence of declarations
@@ -3076,6 +3083,7 @@ def _parse_declarations(
30763083
is_typedef,
30773084
is_friend,
30783085
attributes,
3086+
allow_fields,
30793087
):
30803088
# if it returns True then it handled the end of the statement
30813089
break

tests/test_template.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Note: testcases generated via `python -m cxxheaderparser.gentest`
22

3+
import pytest
4+
5+
from cxxheaderparser.errors import CxxParseError
36
from cxxheaderparser.types import (
47
Array,
58
BaseClass,
@@ -1816,6 +1819,82 @@ class C {
18161819
)
18171820

18181821

1822+
def test_template_function_instantiation() -> None:
1823+
content = """
1824+
template <typename T> T tFunction(T val);
1825+
template <typename T> T tFunction(T val) { return val; }
1826+
template bool tFunction(bool val);
1827+
"""
1828+
data = parse_string(content, cleandoc=True)
1829+
1830+
assert data == ParsedData(
1831+
namespace=NamespaceScope(
1832+
functions=[
1833+
Function(
1834+
return_type=Type(
1835+
typename=PQName(segments=[NameSpecifier(name="T")])
1836+
),
1837+
name=PQName(segments=[NameSpecifier(name="tFunction")]),
1838+
parameters=[
1839+
Parameter(
1840+
type=Type(
1841+
typename=PQName(segments=[NameSpecifier(name="T")])
1842+
),
1843+
name="val",
1844+
)
1845+
],
1846+
template=TemplateDecl(
1847+
params=[TemplateTypeParam(typekey="typename", name="T")]
1848+
),
1849+
),
1850+
Function(
1851+
return_type=Type(
1852+
typename=PQName(segments=[NameSpecifier(name="T")])
1853+
),
1854+
name=PQName(segments=[NameSpecifier(name="tFunction")]),
1855+
parameters=[
1856+
Parameter(
1857+
type=Type(
1858+
typename=PQName(segments=[NameSpecifier(name="T")])
1859+
),
1860+
name="val",
1861+
)
1862+
],
1863+
has_body=True,
1864+
template=TemplateDecl(
1865+
params=[TemplateTypeParam(typekey="typename", name="T")]
1866+
),
1867+
),
1868+
Function(
1869+
return_type=Type(
1870+
typename=PQName(segments=[FundamentalSpecifier(name="bool")])
1871+
),
1872+
name=PQName(segments=[NameSpecifier(name="tFunction")]),
1873+
parameters=[
1874+
Parameter(
1875+
type=Type(
1876+
typename=PQName(
1877+
segments=[FundamentalSpecifier(name="bool")]
1878+
)
1879+
),
1880+
name="val",
1881+
)
1882+
],
1883+
template=TemplateDecl(),
1884+
),
1885+
]
1886+
)
1887+
)
1888+
1889+
1890+
def test_template_rejects_variable_instantiation() -> None:
1891+
with pytest.raises(CxxParseError):
1892+
parse_string("template int x;")
1893+
1894+
with pytest.raises(CxxParseError):
1895+
parse_string("template int (x);")
1896+
1897+
18191898
def test_template_instantiation() -> None:
18201899
content = """
18211900
template class MyClass<1,2>;

0 commit comments

Comments
 (0)