Support multiple template declarations on a class or function

- Fixes #20
This commit is contained in:
Dustin Spicuzza 2023-10-05 02:24:27 -04:00
parent 175815525f
commit 51d29a0791
3 changed files with 180 additions and 9 deletions

View File

@ -44,6 +44,7 @@ from .types import (
Reference,
TemplateArgument,
TemplateDecl,
TemplateDeclTypeVar,
TemplateInst,
TemplateNonTypeParam,
TemplateParam,
@ -615,7 +616,18 @@ class CxxParser:
template = self._parse_template_decl()
# Check for multiple specializations
tok = self.lex.token()
if tok.type == "template":
templates = [template]
while tok.type == "template":
templates.append(self._parse_template_decl())
tok = self.lex.token()
# Can only be followed by declarations
self._parse_declarations(tok, doxygen, templates)
return
if tok.type == "using":
self._parse_using(tok, doxygen, template)
elif tok.type == "friend":
@ -1094,7 +1106,7 @@ class CxxParser:
typename: PQName,
tok: LexToken,
doxygen: typing.Optional[str],
template: typing.Optional[TemplateDecl],
template: TemplateDeclTypeVar,
typedef: bool,
location: Location,
mods: ParsedTypeModifiers,
@ -1795,7 +1807,7 @@ class CxxParser:
return_type: typing.Optional[DecoratedType],
pqname: PQName,
op: typing.Optional[str],
template: typing.Optional[TemplateDecl],
template: TemplateDeclTypeVar,
doxygen: typing.Optional[str],
location: Location,
constructor: bool,
@ -2168,7 +2180,7 @@ class CxxParser:
mods: ParsedTypeModifiers,
location: Location,
doxygen: typing.Optional[str],
template: typing.Optional[TemplateDecl],
template: TemplateDeclTypeVar,
is_typedef: bool,
is_friend: bool,
) -> bool:
@ -2295,6 +2307,9 @@ class CxxParser:
if not dtype:
raise CxxParseError("appear to be parsing a field without a type")
if isinstance(template, list):
raise CxxParseError("multiple template declarations on a field")
self._parse_field(mods, dtype, pqname, template, doxygen, location, is_typedef)
return False
@ -2303,7 +2318,7 @@ class CxxParser:
mods: ParsedTypeModifiers,
location: Location,
doxygen: typing.Optional[str],
template: typing.Optional[TemplateDecl],
template: TemplateDeclTypeVar,
is_typedef: bool,
is_friend: bool,
) -> None:
@ -2356,7 +2371,7 @@ class CxxParser:
self,
tok: LexToken,
doxygen: typing.Optional[str],
template: typing.Optional[TemplateDecl] = None,
template: TemplateDeclTypeVar = None,
is_typedef: bool = False,
is_friend: bool = False,
) -> None:
@ -2426,7 +2441,7 @@ class CxxParser:
parsed_type: Type,
mods: ParsedTypeModifiers,
doxygen: typing.Optional[str],
template: typing.Optional[TemplateDecl],
template: TemplateDeclTypeVar,
is_typedef: bool,
is_friend: bool,
location: Location,

View File

@ -514,6 +514,26 @@ class TemplateDecl:
params: typing.List[TemplateParam] = field(default_factory=list)
#: If no template, this is None. This is a TemplateDecl if this there is a single
#: declaration:
#:
#: .. code-block:: c++
#:
#: template <typename T>
#: struct C {};
#:
#: If there are multiple template declarations, then this is a list of
#: declarations in the order that they're encountered:
#:
#: .. code-block:: c++
#:
#: template<>
#: template<class U>
#: struct A<char>::C {};
#:
TemplateDeclTypeVar = typing.Union[None, TemplateDecl, typing.List[TemplateDecl]]
@dataclass
class TemplateInst:
"""
@ -538,7 +558,7 @@ class ForwardDecl:
"""
typename: PQName
template: typing.Optional[TemplateDecl] = None
template: TemplateDeclTypeVar = None
doxygen: typing.Optional[str] = None
#: Set if this is a forward declaration of an enum and it has a base
@ -576,7 +596,7 @@ class ClassDecl:
typename: PQName
bases: typing.List[BaseClass] = field(default_factory=list)
template: typing.Optional[TemplateDecl] = None
template: TemplateDeclTypeVar = None
explicit: bool = False
final: bool = False
@ -642,7 +662,7 @@ class Function:
#: whatever the trailing return type was
has_trailing_return: bool = False
template: typing.Optional[TemplateDecl] = None
template: TemplateDeclTypeVar = None
#: Value of any throw specification for this function. The value omits the
#: outer parentheses.

View File

@ -2027,3 +2027,139 @@ def test_fwd_declared_method() -> None:
]
)
)
def test_multiple_explicit_member_specialization() -> None:
content = """
template <>
template <>
inline Standard_CString
StdObjMgt_Attribute<TDF_TagSource>::Simple<Standard_Integer>::PName() const {
return "PDF_TagSource";
}
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
method_impls=[
Method(
return_type=Type(
typename=PQName(
segments=[NameSpecifier(name="Standard_CString")]
)
),
name=PQName(
segments=[
NameSpecifier(
name="StdObjMgt_Attribute",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="TDF_TagSource"
)
]
)
)
)
]
),
),
NameSpecifier(
name="Simple",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="Standard_Integer"
)
]
)
)
)
]
),
),
NameSpecifier(name="PName"),
]
),
parameters=[],
inline=True,
has_body=True,
template=[TemplateDecl(), TemplateDecl()],
const=True,
)
]
)
)
def test_member_class_template_specialization() -> None:
content = """
template <> // specialization of a member class template
template <class U>
struct A<char>::C {
void f();
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[
NameSpecifier(
name="A",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="char"
)
]
)
)
)
]
),
),
NameSpecifier(name="C"),
],
classkey="struct",
),
template=[
TemplateDecl(),
TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="U")]
),
],
),
methods=[
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="f")]),
parameters=[],
access="public",
)
],
)
]
)
)