From cf7ea8076868c13e2dcf27d98c0324c5e5307028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Fri, 14 Apr 2017 14:53:37 +0200 Subject: [PATCH] Fix issue with out-of-line constructor definition of template class --- src/libclang/function_parser.cpp | 39 +++++++++++++---- test/cpp_member_function.cpp | 72 +++++++++++++++++++++----------- 2 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index de1b66e..836c3c9 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -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 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 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 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"); diff --git a/test/cpp_member_function.cpp b/test/cpp_member_function.cpp index 19c06d2..e489766 100644 --- a/test/cpp_member_function.cpp +++ b/test/cpp_member_function.cpp @@ -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 struct foo { @@ -232,6 +237,10 @@ struct foo /// constexpr foo(int,char)=delete; constexpr foo(int, char) = delete; }; + +/// foo::foo(int); +template +foo::foo(int) {} )"; } @@ -239,37 +248,50 @@ struct foo auto file = parse(idx, "cpp_constructor.cpp", code); auto count = test_visit(*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::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")