Fix issue with out-of-line constructor definition of template class
This commit is contained in:
parent
06cf090b35
commit
cf7ea80768
2 changed files with 78 additions and 33 deletions
|
|
@ -92,28 +92,51 @@ namespace
|
|||
bool is_explicit = false;
|
||||
};
|
||||
|
||||
bool prefix_end(detail::token_stream& stream, const char* name)
|
||||
bool prefix_end(detail::token_stream& stream, const char* name, bool is_ctor)
|
||||
{
|
||||
auto cur = stream.cur();
|
||||
// name can have multiple tokens if it is an operator
|
||||
if (!detail::skip_if(stream, name, true))
|
||||
return false;
|
||||
else if (!is_ctor)
|
||||
return true;
|
||||
// if we reach this point, we've encountered the name of a constructor
|
||||
// need to make sure it is not actually a class name
|
||||
else if (stream.peek() == "::")
|
||||
{
|
||||
// was a class name of constructor
|
||||
// after name came "::", it is a class name
|
||||
stream.set_cur(cur);
|
||||
return false;
|
||||
}
|
||||
else if (stream.peek() == "<")
|
||||
{
|
||||
// after name came "<", it might be arguments for a class template,
|
||||
// or just a specialization
|
||||
// check if ( comes after the arguments
|
||||
detail::skip_brackets(stream);
|
||||
if (stream.peek() == "(")
|
||||
{
|
||||
// it was just a specialization, we're at the end
|
||||
stream.set_cur(cur);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// class arguments
|
||||
stream.set_cur(cur);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
prefix_info parse_prefix_info(detail::token_stream& stream, const char* name)
|
||||
prefix_info parse_prefix_info(detail::token_stream& stream, const char* name, bool is_ctor)
|
||||
{
|
||||
prefix_info result;
|
||||
|
||||
std::string scope;
|
||||
while (!stream.done() && !prefix_end(stream, name))
|
||||
while (!stream.done() && !prefix_end(stream, name, is_ctor))
|
||||
{
|
||||
if (detail::skip_if(stream, "constexpr"))
|
||||
{
|
||||
|
|
@ -365,7 +388,7 @@ namespace
|
|||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
auto prefix = parse_prefix_info(stream, name.c_str(), false);
|
||||
DEBUG_ASSERT(!prefix.is_virtual && !prefix.is_explicit, detail::parse_error_handler{}, cur,
|
||||
"free function cannot be virtual or explicit");
|
||||
|
||||
|
|
@ -516,7 +539,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
|
|||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
auto prefix = parse_prefix_info(stream, name.c_str(), false);
|
||||
DEBUG_ASSERT(!prefix.is_explicit, detail::parse_error_handler{}, cur,
|
||||
"member function cannot be explicit");
|
||||
|
||||
|
|
@ -545,7 +568,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
|
|||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, "operator");
|
||||
auto prefix = parse_prefix_info(stream, "operator", false);
|
||||
// heuristic to find arguments tokens
|
||||
// skip forward, skipping inside brackets
|
||||
auto type_start = stream.cur();
|
||||
|
|
@ -606,7 +629,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
|
|||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
auto prefix = parse_prefix_info(stream, name.c_str(), true);
|
||||
DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
|
||||
"constructor cannot be virtual");
|
||||
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ TEST_CASE("cpp_constructor")
|
|||
{
|
||||
// only test constructor specific stuff
|
||||
const char* code;
|
||||
auto is_template = false;
|
||||
SECTION("non-template")
|
||||
{
|
||||
code = R"(
|
||||
|
|
@ -217,11 +218,15 @@ struct foo
|
|||
/// constexpr foo(int,char)=delete;
|
||||
constexpr foo(int, char) = delete;
|
||||
};
|
||||
|
||||
/// foo::foo(int);
|
||||
foo::foo(int) {}
|
||||
)";
|
||||
}
|
||||
SECTION("template")
|
||||
{
|
||||
code = R"(
|
||||
is_template = true;
|
||||
code = R"(
|
||||
template <typename T>
|
||||
struct foo
|
||||
{
|
||||
|
|
@ -232,6 +237,10 @@ struct foo
|
|||
/// constexpr foo(int,char)=delete;
|
||||
constexpr foo(int, char) = delete;
|
||||
};
|
||||
|
||||
/// foo<T>::foo(int);
|
||||
template <typename T>
|
||||
foo<T>::foo(int) {}
|
||||
)";
|
||||
}
|
||||
|
||||
|
|
@ -239,37 +248,50 @@ struct foo
|
|||
auto file = parse(idx, "cpp_constructor.cpp", code);
|
||||
auto count = test_visit<cpp_constructor>(*file, [&](const cpp_constructor& cont) {
|
||||
REQUIRE(!cont.is_variadic());
|
||||
REQUIRE(cont.name() == "foo");
|
||||
|
||||
if (count_children(cont.parameters()) == 0u)
|
||||
{
|
||||
REQUIRE(cont.noexcept_condition());
|
||||
REQUIRE(
|
||||
equal_expressions(cont.noexcept_condition().value(),
|
||||
*cpp_literal_expression::build(cpp_builtin_type::build(cpp_bool),
|
||||
"true")));
|
||||
REQUIRE(!cont.is_explicit());
|
||||
REQUIRE(!cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_defaulted);
|
||||
}
|
||||
else if (count_children(cont.parameters()) == 1u)
|
||||
if (cont.is_definition() && count_children(cont.parameters()) == 1u)
|
||||
{
|
||||
if (is_template)
|
||||
REQUIRE(cont.name() == "foo<T>::foo");
|
||||
else
|
||||
REQUIRE(cont.name() == "foo::foo");
|
||||
REQUIRE(!cont.noexcept_condition());
|
||||
REQUIRE(cont.is_explicit());
|
||||
REQUIRE(!cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_declaration);
|
||||
}
|
||||
else if (count_children(cont.parameters()) == 2u)
|
||||
{
|
||||
REQUIRE(!cont.noexcept_condition());
|
||||
REQUIRE(!cont.is_explicit());
|
||||
REQUIRE(cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_deleted);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
{
|
||||
REQUIRE(cont.name() == "foo");
|
||||
|
||||
if (count_children(cont.parameters()) == 0u)
|
||||
{
|
||||
REQUIRE(cont.noexcept_condition());
|
||||
REQUIRE(equal_expressions(cont.noexcept_condition().value(),
|
||||
*cpp_literal_expression::build(cpp_builtin_type::build(
|
||||
cpp_bool),
|
||||
"true")));
|
||||
REQUIRE(!cont.is_explicit());
|
||||
REQUIRE(!cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_defaulted);
|
||||
}
|
||||
else if (count_children(cont.parameters()) == 1u)
|
||||
{
|
||||
REQUIRE(!cont.noexcept_condition());
|
||||
REQUIRE(cont.is_explicit());
|
||||
REQUIRE(!cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_declaration);
|
||||
}
|
||||
else if (count_children(cont.parameters()) == 2u)
|
||||
{
|
||||
REQUIRE(!cont.noexcept_condition());
|
||||
REQUIRE(!cont.is_explicit());
|
||||
REQUIRE(cont.is_constexpr());
|
||||
REQUIRE(cont.body_kind() == cpp_function_deleted);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
});
|
||||
REQUIRE(count == 3u);
|
||||
REQUIRE(count == 4u);
|
||||
}
|
||||
|
||||
TEST_CASE("cpp_destructor")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue