cxxheaderparser/tests/test_fn.py
Dustin Spicuzza c3fbe4c16c breaking change: Remove operator type and make part of Function
- Unifies free function operators and method operators -- otherwise users
  would need to parse the operator themselves to deal with free function operators
- We don't have any releases yet, so there are no stability guarantees
2023-09-23 21:45:30 -04:00

1144 lines
41 KiB
Python

# Note: testcases generated via `python -m cxxheaderparser.gentest`
from cxxheaderparser.types import (
Array,
AutoSpecifier,
ClassDecl,
Function,
FunctionType,
FundamentalSpecifier,
Method,
MoveReference,
NameSpecifier,
PQName,
Parameter,
Pointer,
Reference,
TemplateArgument,
TemplateDecl,
TemplateSpecialization,
TemplateTypeParam,
Token,
Type,
Typedef,
Value,
)
from cxxheaderparser.simple import (
ClassScope,
NamespaceScope,
parse_string,
ParsedData,
)
def test_fn_returns_class() -> None:
content = """
class X *fn1();
struct Y fn2();
enum E fn3();
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[NameSpecifier(name="X")], classkey="class"
)
)
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[],
),
Function(
return_type=Type(
typename=PQName(
segments=[NameSpecifier(name="Y")], classkey="struct"
)
),
name=PQName(segments=[NameSpecifier(name="fn2")]),
parameters=[],
),
Function(
return_type=Type(
typename=PQName(
segments=[NameSpecifier(name="E")], classkey="enum"
)
),
name=PQName(segments=[NameSpecifier(name="fn3")]),
parameters=[],
),
]
)
)
def test_fn_returns_typename() -> None:
content = """
typename ns::X fn();
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="ns"),
NameSpecifier(name="X"),
],
has_typename=True,
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
)
]
)
)
def test_fn_returns_typename_const() -> None:
content = """
const typename ns::X fn();
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="ns"),
NameSpecifier(name="X"),
],
has_typename=True,
),
const=True,
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
)
]
)
)
def test_fn_pointer_params() -> None:
content = """
int fn1(int *);
int fn2(int *p);
int fn3(int(*p));
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[
Parameter(
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
)
),
)
],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn2")]),
parameters=[
Parameter(
name="p",
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
)
),
)
],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn3")]),
parameters=[
Parameter(
name="p",
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
)
),
)
],
),
]
)
)
def test_fn_void_is_no_params() -> None:
content = """
int fn(void);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
)
]
)
)
def test_fn_array_param() -> None:
content = """
void fn(int array[]);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[
Parameter(
name="array",
type=Array(
array_of=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
),
size=None,
),
)
],
)
]
)
)
def test_fn_typename_param() -> None:
content = """
void MethodA(const mynamespace::SomeObject &x,
typename mynamespace::SomeObject * = 0);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="MethodA")]),
parameters=[
Parameter(
type=Reference(
ref_to=Type(
typename=PQName(
segments=[
NameSpecifier(name="mynamespace"),
NameSpecifier(name="SomeObject"),
]
),
const=True,
)
),
name="x",
),
Parameter(
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[
NameSpecifier(name="mynamespace"),
NameSpecifier(name="SomeObject"),
],
has_typename=True,
)
)
),
default=Value(tokens=[Token(value="0")]),
),
],
)
]
)
)
def test_fn_weird_refs() -> None:
content = """
int aref(int(&x));
void ptr_ref(int(*&name));
void ref_to_array(int (&array)[]);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="aref")]),
parameters=[
Parameter(
name="x",
type=Reference(
ref_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
)
),
)
],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="ptr_ref")]),
parameters=[
Parameter(
name="name",
type=Reference(
ref_to=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
)
)
),
)
],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="ref_to_array")]),
parameters=[
Parameter(
name="array",
type=Reference(
ref_to=Array(
array_of=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
),
size=None,
)
),
)
],
),
]
)
)
def test_fn_too_many_parens() -> None:
content = """
int fn1(int (x));
void (fn2 (int (*const (name))));
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[
Parameter(
name="x",
type=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
),
)
],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn2")]),
parameters=[
Parameter(
name="name",
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
)
),
const=True,
),
)
],
),
]
)
)
# TODO calling conventions
"""
void __stdcall fn();
void (__stdcall * fn)
"""
def test_fn_same_line() -> None:
# multiple functions on the same line
content = """
void fn1(), fn2();
void *fn3(), fn4();
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn2")]),
parameters=[],
),
Function(
return_type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="void")]
)
)
),
name=PQName(segments=[NameSpecifier(name="fn3")]),
parameters=[],
),
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn4")]),
parameters=[],
),
]
)
)
def test_fn_auto_template() -> None:
content = """
template<class T, class U>
auto add(T t, U u) { return t + u; }
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(typename=PQName(segments=[AutoSpecifier()])),
name=PQName(segments=[NameSpecifier(name="add")]),
parameters=[
Parameter(
type=Type(
typename=PQName(segments=[NameSpecifier(name="T")])
),
name="t",
),
Parameter(
type=Type(
typename=PQName(segments=[NameSpecifier(name="U")])
),
name="u",
),
],
has_body=True,
template=TemplateDecl(
params=[
TemplateTypeParam(typekey="class", name="T"),
TemplateTypeParam(typekey="class", name="U"),
]
),
)
]
)
)
def test_fn_template_ptr() -> None:
content = """
std::vector<Pointer *> *fn(std::vector<Pointer *> *ps);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="vector",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Pointer(
ptr_to=Type(
typename=PQName(
segments=[
NameSpecifier(
name="Pointer"
)
]
)
)
)
)
]
),
),
]
)
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[
Parameter(
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="vector",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=Pointer(
ptr_to=Type(
typename=PQName(
segments=[
NameSpecifier(
name="Pointer"
)
]
)
)
)
)
]
),
),
]
)
)
),
name="ps",
)
],
)
]
)
)
def test_fn_with_impl() -> None:
content = """
// clang-format off
void termite(void)
{
return ((structA*) (Func())->element);
}
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="termite")]),
parameters=[],
has_body=True,
)
]
)
)
def test_fn_return_std_function() -> None:
content = """
std::function<void(int)> fn();
"""
data1 = parse_string(content, cleandoc=True)
content = """
std::function<void((int))> fn();
"""
data2 = parse_string(content, cleandoc=True)
expected = ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="function",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=FunctionType(
return_type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="void"
)
]
)
),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="int"
)
]
)
)
)
],
)
)
]
),
),
]
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
)
]
)
)
assert data1 == expected
assert data2 == expected
def test_fn_return_std_function_trailing() -> None:
content = """
std::function<auto(int)->int> fn();
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="function",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=FunctionType(
return_type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="int"
)
]
)
),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="int"
)
]
)
)
)
],
has_trailing_return=True,
)
)
]
),
),
]
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
)
]
)
)
def test_fn_trailing_return_simple() -> None:
content = """
auto fn() -> int;
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
has_trailing_return=True,
)
]
)
)
def test_fn_trailing_return_std_function() -> None:
content = """
auto fn() -> std::function<int()>;
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="function",
specialization=TemplateSpecialization(
args=[
TemplateArgument(
arg=FunctionType(
return_type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(
name="int"
)
]
)
),
parameters=[],
)
)
]
),
),
]
)
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
has_trailing_return=True,
)
]
)
)
def test_inline_volatile_fn() -> None:
content = """
inline int Standard_Atomic_Increment (volatile int* theValue);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="int")])
),
name=PQName(
segments=[NameSpecifier(name="Standard_Atomic_Increment")]
),
parameters=[
Parameter(
type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="int")]
),
volatile=True,
)
),
name="theValue",
)
],
inline=True,
)
]
)
)
def test_method_w_reference() -> None:
content = """
struct StreamBuffer
{
StreamBuffer &operator<<(std::ostream &(*fn)(std::ostream &))
{
return *this;
}
};
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
classes=[
ClassScope(
class_decl=ClassDecl(
typename=PQName(
segments=[NameSpecifier(name="StreamBuffer")],
classkey="struct",
)
),
methods=[
Method(
return_type=Reference(
ref_to=Type(
typename=PQName(
segments=[NameSpecifier(name="StreamBuffer")]
)
)
),
name=PQName(segments=[NameSpecifier(name="operator<<")]),
parameters=[
Parameter(
type=Pointer(
ptr_to=FunctionType(
return_type=Reference(
ref_to=Type(
typename=PQName(
segments=[
NameSpecifier(name="std"),
NameSpecifier(
name="ostream"
),
]
)
)
),
parameters=[
Parameter(
type=Reference(
ref_to=Type(
typename=PQName(
segments=[
NameSpecifier(
name="std"
),
NameSpecifier(
name="ostream"
),
]
)
)
)
)
],
)
),
name="fn",
)
],
has_body=True,
access="public",
operator="<<",
)
],
)
]
)
)
def test_fn_w_mvreference() -> None:
content = """
void fn1(int && (*)(int));
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn1")]),
parameters=[
Parameter(
type=Pointer(
ptr_to=FunctionType(
return_type=MoveReference(
moveref_to=Type(
typename=PQName(
segments=[
FundamentalSpecifier(name="int")
]
)
)
),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[
FundamentalSpecifier(name="int")
]
)
)
)
],
)
)
)
],
)
]
)
)
def test_msvc_conventions() -> None:
content = """
void __cdecl fn();
typedef const char* (__stdcall *wglGetExtensionsStringARB_t)(HDC theDeviceContext);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="fn")]),
parameters=[],
msvc_convention="__cdecl",
)
],
typedefs=[
Typedef(
type=Pointer(
ptr_to=FunctionType(
return_type=Pointer(
ptr_to=Type(
typename=PQName(
segments=[FundamentalSpecifier(name="char")]
),
const=True,
)
),
parameters=[
Parameter(
type=Type(
typename=PQName(
segments=[NameSpecifier(name="HDC")]
)
),
name="theDeviceContext",
)
],
msvc_convention="__stdcall",
)
),
name="wglGetExtensionsStringARB_t",
)
],
)
)
def test_throw_empty() -> None:
content = """
void foo() throw() { throw std::runtime_error("foo"); }
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="foo")]),
parameters=[],
has_body=True,
throw=Value(tokens=[]),
)
]
)
)
def test_throw_dynamic() -> None:
content = """
void foo() throw(std::exception) { throw std::runtime_error("foo"); }
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="foo")]),
parameters=[],
has_body=True,
throw=Value(
tokens=[
Token(value="std"),
Token(value="::"),
Token(value="exception"),
]
),
)
]
)
)
def test_noexcept_empty() -> None:
content = """
void foo() noexcept;
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="foo")]),
parameters=[],
noexcept=Value(tokens=[]),
)
]
)
)
def test_noexcept_contents() -> None:
content = """
void foo() noexcept(false);
"""
data = parse_string(content, cleandoc=True)
assert data == ParsedData(
namespace=NamespaceScope(
functions=[
Function(
return_type=Type(
typename=PQName(segments=[FundamentalSpecifier(name="void")])
),
name=PQName(segments=[NameSpecifier(name="foo")]),
parameters=[],
noexcept=Value(tokens=[Token(value="false")]),
)
]
)
)