Add support for parsing explicit template instantiation

This commit is contained in:
Dustin Spicuzza 2022-12-06 08:44:28 -05:00
parent f1c1f2e6af
commit ddad7cb6b1
5 changed files with 256 additions and 8 deletions

View File

@ -43,6 +43,7 @@ from .types import (
Reference,
TemplateArgument,
TemplateDecl,
TemplateInst,
TemplateNonTypeParam,
TemplateParam,
TemplateSpecialization,
@ -406,16 +407,21 @@ class CxxParser:
def _parse_extern(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
etok = self.lex.token_if("STRING_LITERAL")
etok = self.lex.token_if("STRING_LITERAL", "template")
if etok:
if self.lex.token_if("{"):
state = self._push_state(ExternBlockState, etok.value)
state.location = tok.location
self.visitor.on_extern_block_start(state)
return
if etok.type == "STRING_LITERAL":
if self.lex.token_if("{"):
state = self._push_state(ExternBlockState, etok.value)
state.location = tok.location
self.visitor.on_extern_block_start(state)
return
# an extern variable/function with specific linkage
self.lex.return_token(etok)
# an extern variable/function with specific linkage
self.lex.return_token(etok)
else:
# must be an extern template instantitation
self._parse_template_instantiation(doxygen, True)
return
self._parse_declarations(tok, doxygen)
@ -547,6 +553,9 @@ class CxxParser:
return TemplateDecl(params)
def _parse_template(self, tok: LexToken, doxygen: typing.Optional[str]) -> None:
if not self.lex.token_peek_if("<"):
self._parse_template_instantiation(doxygen, False)
return
template = self._parse_template_decl()
@ -624,6 +633,41 @@ class CxxParser:
return TemplateSpecialization(args)
def _parse_template_instantiation(
self, doxygen: typing.Optional[str], extern: bool
):
"""
explicit-instantiation: [extern] template declaration
"""
# entry is right after template
tok = self.lex.token_if("class", "struct")
if not tok:
raise self._parse_error(tok)
atok = self.lex.token_if_in_set(self._attribute_start_tokens)
if atok:
self._consume_attribute(atok)
typename, _ = self._parse_pqname(None)
# the last segment must have a specialization
last_segment = typename.segments[-1]
if (
not isinstance(last_segment, NameSpecifier)
or not last_segment.specialization
):
raise self._parse_error(
None, "expected extern template to have specialization"
)
self._next_token_must_be(";")
self.visitor.on_template_inst(
self.state, TemplateInst(typename, extern, doxygen)
)
#
# Attributes
#

View File

@ -39,6 +39,7 @@ from .types import (
FriendDecl,
Function,
Method,
TemplateInst,
Typedef,
UsingAlias,
UsingDecl,
@ -102,6 +103,9 @@ class NamespaceScope:
using_ns: typing.List["UsingNamespace"] = field(default_factory=list)
using_alias: typing.List[UsingAlias] = field(default_factory=list)
#: Explicit template instantiations
template_insts: typing.List[TemplateInst] = field(default_factory=list)
#: Child namespaces
namespaces: typing.Dict[str, "NamespaceScope"] = field(default_factory=dict)
@ -248,6 +252,10 @@ class SimpleCxxVisitor:
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
self.block.forward_decls.append(fdecl)
def on_template_inst(self, state: State, inst: TemplateInst) -> None:
assert isinstance(self.block, NamespaceScope)
self.block.template_insts.append(inst)
def on_variable(self, state: State, v: Variable) -> None:
assert isinstance(self.block, NamespaceScope)
self.block.variables.append(v)

View File

@ -390,6 +390,23 @@ class TemplateDecl:
params: typing.List[TemplateParam] = field(default_factory=list)
@dataclass
class TemplateInst:
"""
Explicit template instantiation
.. code-block:: c++
template class MyClass<1,2>;
extern template class MyClass<2,3>;
"""
typename: PQName
extern: bool
doxygen: typing.Optional[str] = None
@dataclass
class ForwardDecl:
"""

View File

@ -14,6 +14,7 @@ from .types import (
FriendDecl,
Function,
Method,
TemplateInst,
Typedef,
UsingAlias,
UsingDecl,
@ -94,6 +95,11 @@ class CxxVisitor(Protocol):
Called when a forward declaration is encountered
"""
def on_template_inst(self, state: State, inst: TemplateInst) -> None:
"""
Called when an explicit template instantiation is encountered
"""
def on_variable(self, state: State, v: Variable) -> None:
...

View File

@ -18,6 +18,7 @@ from cxxheaderparser.types import (
Reference,
TemplateArgument,
TemplateDecl,
TemplateInst,
TemplateNonTypeParam,
TemplateSpecialization,
TemplateTypeParam,
@ -1814,3 +1815,175 @@ def test_template_specialized_fn_typename_template() -> None:
]
)
)
def test_template_instantiation() -> None:
content = """
template class MyClass<1,2>;
template class __attribute__(("something")) MyClass<3,4>;
namespace foo {
template class MyClass<5,6>;
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
template_insts=[
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="1")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="2")])
),
]
),
)
]
),
extern=False,
),
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="3")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="4")])
),
]
),
)
]
),
extern=False,
),
],
namespaces={
"foo": NamespaceScope(
name="foo",
template_insts=[
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="5")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="6")])
),
]
),
)
]
),
extern=False,
)
],
)
},
)
)
def test_extern_template() -> None:
content = """
extern template class MyClass<1,2>;
extern template class __attribute__(("something")) MyClass<3,4>;
namespace foo {
extern template class MyClass<5,6>;
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
template_insts=[
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="1")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="2")])
),
]
),
)
]
),
extern=True,
),
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="3")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="4")])
),
]
),
)
]
),
extern=True,
),
],
namespaces={
"foo": NamespaceScope(
name="foo",
template_insts=[
TemplateInst(
typename=PQName(
segments=[
NameSpecifier(
name="MyClass",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Value(tokens=[Token(value="5")])
),
TemplateArgument(
arg=Value(tokens=[Token(value="6")])
),
]
),
)
]
),
extern=True,
)
],
)
},
)
)