From 39b5b01ae3f56ba3474b79855efef9ca06054014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sun, 12 Mar 2017 12:28:11 +0100 Subject: [PATCH] Parse static class functions --- src/libclang/function_parser.cpp | 90 +++++++++++++++++++------------- src/libclang/parse_functions.cpp | 5 ++ src/libclang/parse_functions.hpp | 8 ++- test/cpp_function.cpp | 54 +++++++++++++++++++ 4 files changed, 118 insertions(+), 39 deletions(-) diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 203c6b4..ad1fb45 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -82,47 +82,63 @@ namespace "unexpected token for function body kind"); return cpp_function_declaration; } + + std::unique_ptr parse_cpp_function_impl(const detail::parse_context& context, + const CXCursor& cur) + { + auto name = detail::get_cursor_name(cur); + + cpp_function::builder builder(name.c_str(), + detail::parse_type(context, clang_getCursorResultType(cur))); + add_parameters(context, builder, cur); + if (clang_Cursor_isVariadic(cur)) + builder.is_variadic(); + builder.storage_class(detail::get_storage_class(cur)); + + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + + // parse prefix + while (!detail::skip_if(stream, name.c_str())) + { + if (detail::skip_if(stream, "constexpr")) + builder.is_constexpr(); + else + stream.bump(); + } + // skip parameters + skip_parameters(stream); + + auto body = + clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration; + // parse suffix + // tokenizer only tokenizes signature, so !stream.done() is sufficient + while (!stream.done()) + { + if (auto expr = try_parse_noexcept(stream, context)) + builder.noexcept_condition(std::move(expr)); + else if (skip_if(stream, "=")) + body = parse_body_kind(stream); + else + stream.bump(); + } + + return builder.finish(*context.idx, detail::get_entity_id(cur), body); + } } std::unique_ptr detail::parse_cpp_function(const detail::parse_context& context, const CXCursor& cur) { DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl, detail::assert_handler{}); - auto name = detail::get_cursor_name(cur); - - cpp_function::builder builder(name.c_str(), - detail::parse_type(context, clang_getCursorResultType(cur))); - add_parameters(context, builder, cur); - if (clang_Cursor_isVariadic(cur)) - builder.is_variadic(); - builder.storage_class(detail::get_storage_class(cur)); - - detail::tokenizer tokenizer(context.tu, context.file, cur); - detail::token_stream stream(tokenizer, cur); - - // parse prefix - while (!detail::skip_if(stream, name.c_str())) - { - if (detail::skip_if(stream, "constexpr")) - builder.is_constexpr(); - else - stream.bump(); - } - // skip parameters - skip_parameters(stream); - - auto body = clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration; - // parse suffix - // tokenizer only tokenizes signature, so !stream.done() is sufficient - while (!stream.done()) - { - if (auto expr = try_parse_noexcept(stream, context)) - builder.noexcept_condition(std::move(expr)); - else if (skip_if(stream, "=")) - body = parse_body_kind(stream); - else - stream.bump(); - } - - return builder.finish(*context.idx, detail::get_entity_id(cur), body); + return parse_cpp_function_impl(context, cur); +} + +std::unique_ptr detail::try_parse_static_cpp_function( + const detail::parse_context& context, const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod, detail::assert_handler{}); + if (clang_CXXMethod_isStatic(cur)) + return parse_cpp_function_impl(context, cur); + return nullptr; } diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index fac03ff..812bf22 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -87,6 +87,11 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_FunctionDecl: return parse_cpp_function(context, cur); + case CXCursor_CXXMethod: + // check for static function + if (auto func = try_parse_static_cpp_function(context, cur)) + return func; + break; default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 1c2fd8c..f911ea4 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -53,11 +53,15 @@ namespace cppast // parse_entity() dispatches on the cursor type // it calls one of the other parse functions defined elsewhere - // try_parse_XXX are not exposed entities - // they are called on an unexposed cursor and see whether they match + // try_parse_XXX are not exposed/differently exposed entities + // they are called on corresponding cursor and see whether they match + // unexposed std::unique_ptr try_parse_cpp_language_linkage(const parse_context& context, const CXCursor& cur); + // CXXMethod + std::unique_ptr try_parse_static_cpp_function(const parse_context& context, + const CXCursor& cur); std::unique_ptr parse_cpp_namespace(const parse_context& context, const CXCursor& cur); diff --git a/test/cpp_function.cpp b/test/cpp_function.cpp index 70865e0..68b2d93 100644 --- a/test/cpp_function.cpp +++ b/test/cpp_function.cpp @@ -193,3 +193,57 @@ void l() }); REQUIRE(count == 12u); } + +TEST_CASE("static cpp_function") +{ + auto code = R"( +// no need to test anything special +struct foo +{ + static void a(); + + static int b() noexcept {} + + static constexpr char c() = delete; +}; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "static_cpp_function.cpp", code); + auto count = test_visit(*file, [&](const cpp_function& func) { + REQUIRE(!func.is_variadic()); + REQUIRE(count_children(func) == 0u); + REQUIRE(func.storage_class() == cpp_storage_class_static); + + if (func.name() == "a") + { + REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("void"))); + REQUIRE(!func.noexcept_condition()); + REQUIRE(!func.is_constexpr()); + REQUIRE(func.body_kind() == cpp_function_declaration); + } + else if (func.name() == "b") + { + REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("int"))); + REQUIRE(func.noexcept_condition()); + REQUIRE( + equal_expressions(func.noexcept_condition().value(), + *cpp_literal_expression::build(cpp_builtin_type::build("bool"), + "true"))); + REQUIRE(!func.is_constexpr()); + REQUIRE(func.body_kind() == cpp_function_definition); + } + else if (func.name() == "c") + { + REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("char"))); + REQUIRE(!func.noexcept_condition()); + REQUIRE(func.is_constexpr()); + REQUIRE(func.body_kind() == cpp_function_deleted); + } + else + REQUIRE(false); + }); + REQUIRE(count == 3u); +} + +// TODO: friend functions (clang 4.0 required)