Merge pull request #39 from robotpy/more-cppheaderparser-tests

More cppheaderparser tests
This commit is contained in:
Dustin Spicuzza 2022-12-07 23:03:58 -05:00 committed by GitHub
commit 2c238058d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 641 additions and 11 deletions

View File

@ -60,14 +60,14 @@ def gentest(infile: str, name: str, outfile: str, verbose: bool) -> None:
stmt = nondefault_repr(data)
content = content.replace("\n", "\n ")
content = ("\n" + content.strip()).replace("\n", "\n ")
content = "\n".join(l.rstrip() for l in content.splitlines())
stmt = inspect.cleandoc(
f'''
def test_{name}() -> None:
content = """
{content}
content = """{content}
"""
data = parse_string(content, cleandoc=True)

View File

@ -43,6 +43,7 @@ from .types import (
Reference,
TemplateArgument,
TemplateDecl,
TemplateInst,
TemplateNonTypeParam,
TemplateParam,
TemplateSpecialization,
@ -406,8 +407,9 @@ 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 etok.type == "STRING_LITERAL":
if self.lex.token_if("{"):
state = self._push_state(ExternBlockState, etok.value)
state.location = tok.location
@ -416,6 +418,10 @@ class CxxParser:
# 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

@ -2998,3 +2998,217 @@ def test_class_mutable() -> None:
]
)
)
def test_nested_class_access() -> None:
content = """
class Outer {
struct Inner {
void fn();
};
void ofn();
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="Outer")], classkey="class"
)
),
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="Inner")],
classkey="struct",
),
access="private",
),
methods=[
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
access="public",
)
],
)
],
methods=[
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="ofn")]),
parameters=[],
access="private",
)
],
)
]
)
)
def test_class_with_typedef() -> None:
content = """
template <class SomeType> class A {
public:
typedef B <SomeType> C;
A();
protected:
C aCInstance;
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="A")], classkey="class"
),
template=TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="SomeType")]
),
),
fields=[
Field(
access="protected",
type=Type(
typename=PQName(segments=[NameSpecifier(name="C")])
),
name="aCInstance",
)
],
methods=[
Method(
return_type=None,
name=PQName(segments=[NameSpecifier(name="A")]),
parameters=[],
access="public",
constructor=True,
)
],
typedefs=[
Typedef(
type=Type(
typename=PQName(
segments=[
NameSpecifier(
name="B",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="SomeType"
)
]
)
)
)
]
),
)
]
)
),
name="C",
access="public",
)
],
)
]
)
)
def test_class_ref_qualifiers() -> None:
content = """
struct X {
void fn0();
void fn1() &;
void fn2() &&;
void fn3() && = 0;
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="X")], classkey="struct"
)
),
methods=[
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="fn0")]),
parameters=[],
access="public",
),
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[],
access="public",
ref_qualifier="&",
),
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="fn2")]),
parameters=[],
access="public",
ref_qualifier="&&",
),
Method(
return_type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
),
name=PQName(segments=[NameSpecifier(name="fn3")]),
parameters=[],
access="public",
ref_qualifier="&&",
pure_virtual=True,
),
],
)
]
)
)

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,
)
],
)
},
)
)

View File

@ -548,3 +548,171 @@ def test_using_many_things() -> None:
},
)
)
def test_using_template_in_class() -> None:
content = """
class X {
template <typename T>
using TT = U<T>;
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="X")], classkey="class"
)
),
using_alias=[
UsingAlias(
alias="TT",
type=Type(
typename=PQName(
segments=[
NameSpecifier(
name="U",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="T"
)
]
)
)
)
]
),
)
]
)
),
template=TemplateDecl(
params=[TemplateTypeParam(typekey="typename", name="T")]
),
access="private",
)
],
)
]
)
)
def test_using_typename_in_class() -> None:
content = """
template <class D> class P {
using A = typename f::TP<D>::A;
public:
using State = typename f::TP<D>::S;
P(State st);
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="P")], classkey="class"
),
template=TemplateDecl(
params=[TemplateTypeParam(typekey="class", name="D")]
),
),
methods=[
Method(
return_type=None,
name=PQName(segments=[NameSpecifier(name="P")]),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[NameSpecifier(name="State")]
)
),
name="st",
)
],
access="public",
constructor=True,
)
],
using_alias=[
UsingAlias(
alias="A",
type=Type(
typename=PQName(
segments=[
NameSpecifier(name="f"),
NameSpecifier(
name="TP",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="D"
)
]
)
)
)
]
),
),
NameSpecifier(name="A"),
],
has_typename=True,
)
),
access="private",
),
UsingAlias(
alias="State",
type=Type(
typename=PQName(
segments=[
NameSpecifier(name="f"),
NameSpecifier(
name="TP",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Type(
typename=PQName(
segments=[
NameSpecifier(
name="D"
)
]
)
)
)
]
),
),
NameSpecifier(name="S"),
],
has_typename=True,
)
),
access="public",
),
],
)
]
)
)