Fix issue with out-of-line constructor definition of template class

This commit is contained in:
Jonathan Müller 2017-04-14 14:53:37 +02:00
commit cf7ea80768
2 changed files with 78 additions and 33 deletions

View file

@ -92,28 +92,51 @@ namespace
bool is_explicit = false; 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(); auto cur = stream.cur();
// name can have multiple tokens if it is an operator // name can have multiple tokens if it is an operator
if (!detail::skip_if(stream, name, true)) if (!detail::skip_if(stream, name, true))
return false; 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() == "::") else if (stream.peek() == "::")
{ {
// was a class name of constructor // after name came "::", it is a class name
stream.set_cur(cur); stream.set_cur(cur);
return false; 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 else
return true; 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; prefix_info result;
std::string scope; 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")) if (detail::skip_if(stream, "constexpr"))
{ {
@ -365,7 +388,7 @@ namespace
detail::tokenizer tokenizer(context.tu, context.file, cur); detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, 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, DEBUG_ASSERT(!prefix.is_virtual && !prefix.is_explicit, detail::parse_error_handler{}, cur,
"free function cannot be virtual or explicit"); "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::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, 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, DEBUG_ASSERT(!prefix.is_explicit, detail::parse_error_handler{}, cur,
"member function cannot be explicit"); "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::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, 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 // heuristic to find arguments tokens
// skip forward, skipping inside brackets // skip forward, skipping inside brackets
auto type_start = stream.cur(); 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::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, 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, DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
"constructor cannot be virtual"); "constructor cannot be virtual");

View file

@ -205,6 +205,7 @@ TEST_CASE("cpp_constructor")
{ {
// only test constructor specific stuff // only test constructor specific stuff
const char* code; const char* code;
auto is_template = false;
SECTION("non-template") SECTION("non-template")
{ {
code = R"( code = R"(
@ -217,11 +218,15 @@ struct foo
/// constexpr foo(int,char)=delete; /// constexpr foo(int,char)=delete;
constexpr foo(int, char) = delete; constexpr foo(int, char) = delete;
}; };
/// foo::foo(int);
foo::foo(int) {}
)"; )";
} }
SECTION("template") SECTION("template")
{ {
code = R"( is_template = true;
code = R"(
template <typename T> template <typename T>
struct foo struct foo
{ {
@ -232,6 +237,10 @@ struct foo
/// constexpr foo(int,char)=delete; /// constexpr foo(int,char)=delete;
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 file = parse(idx, "cpp_constructor.cpp", code);
auto count = test_visit<cpp_constructor>(*file, [&](const cpp_constructor& cont) { auto count = test_visit<cpp_constructor>(*file, [&](const cpp_constructor& cont) {
REQUIRE(!cont.is_variadic()); REQUIRE(!cont.is_variadic());
REQUIRE(cont.name() == "foo");
if (count_children(cont.parameters()) == 0u) if (cont.is_definition() && count_children(cont.parameters()) == 1u)
{
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 (is_template)
REQUIRE(cont.name() == "foo<T>::foo");
else
REQUIRE(cont.name() == "foo::foo");
REQUIRE(!cont.noexcept_condition()); REQUIRE(!cont.noexcept_condition());
REQUIRE(cont.is_explicit());
REQUIRE(!cont.is_constexpr()); 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 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") TEST_CASE("cpp_destructor")