From c87b296d4edbcf20ec6e962c309308b057244e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 25 Feb 2017 11:17:18 +0100 Subject: [PATCH] Parse cpp_member_variable and cpp_bitfield --- include/cppast/cpp_member_variable.hpp | 31 +++++--- src/cpp_member_variable.cpp | 43 ++++++++++- src/libclang/expression_parser.cpp | 28 +++++-- src/libclang/parse_functions.cpp | 2 + src/libclang/parse_functions.hpp | 10 +++ src/libclang/tokenizer.cpp | 3 +- src/libclang/variable_parser.cpp | 36 +++++++++ test/cpp_member_variable.cpp | 103 +++++++++++++++++++++++++ 8 files changed, 238 insertions(+), 18 deletions(-) create mode 100644 test/cpp_member_variable.cpp diff --git a/include/cppast/cpp_member_variable.hpp b/include/cppast/cpp_member_variable.hpp index ed1a440..89545ab 100644 --- a/include/cppast/cpp_member_variable.hpp +++ b/include/cppast/cpp_member_variable.hpp @@ -37,17 +37,15 @@ namespace cppast class cpp_member_variable final : public cpp_member_variable_base { public: + static cpp_entity_kind kind() noexcept; + /// \returns A newly created and registered member variable. /// \notes `def` may be `nullptr` in which case there is no member initializer provided. - static std::unique_ptr build(std::string name, + static std::unique_ptr build(const cpp_entity_index& idx, + cpp_entity_id id, std::string name, std::unique_ptr type, std::unique_ptr def, - bool is_mutable) - { - return std::unique_ptr( - new cpp_member_variable(std::move(name), std::move(type), std::move(def), - is_mutable)); - } + bool is_mutable); private: using cpp_member_variable_base::cpp_member_variable_base; @@ -59,6 +57,19 @@ namespace cppast class cpp_bitfield final : public cpp_member_variable_base { public: + static cpp_entity_kind kind() noexcept; + + /// \returns A newly created and registered bitfield. + /// \notes It cannot have a member initializer, i.e. default value. + static std::unique_ptr build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, std::unique_ptr type, + unsigned no_bits, bool is_mutable); + + /// \returns A newly created unnamed bitfield. + /// \notes It will not be registered, as it is unnamed. + static std::unique_ptr build(std::unique_ptr type, unsigned no_bits, + bool is_mutable); + /// \returns The number of bits of the bitfield. unsigned no_bits() const noexcept { @@ -66,9 +77,9 @@ namespace cppast } private: - cpp_bitfield(std::string name, std::unique_ptr type, - std::unique_ptr def, unsigned no_bits, bool is_mutable) - : cpp_member_variable_base(std::move(name), std::move(type), std::move(def), is_mutable), + cpp_bitfield(std::string name, std::unique_ptr type, unsigned no_bits, + bool is_mutable) + : cpp_member_variable_base(std::move(name), std::move(type), nullptr, is_mutable), bits_(no_bits) { } diff --git a/src/cpp_member_variable.cpp b/src/cpp_member_variable.cpp index b417773..6875287 100644 --- a/src/cpp_member_variable.cpp +++ b/src/cpp_member_variable.cpp @@ -8,12 +8,51 @@ using namespace cppast; -cpp_entity_kind cpp_member_variable::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_member_variable::kind() noexcept { return cpp_entity_kind::member_variable_t; } -cpp_entity_kind cpp_bitfield::do_get_entity_kind() const noexcept +std::unique_ptr cpp_member_variable::build(const cpp_entity_index& idx, + cpp_entity_id id, std::string name, + std::unique_ptr type, + std::unique_ptr def, + bool is_mutable) +{ + auto result = std::unique_ptr( + new cpp_member_variable(std::move(name), std::move(type), std::move(def), is_mutable)); + idx.register_entity(std::move(id), type_safe::cref(*result)); + return result; +} + +cpp_entity_kind cpp_member_variable::do_get_entity_kind() const noexcept +{ + return kind(); +} + +cpp_entity_kind cpp_bitfield::kind() noexcept { return cpp_entity_kind::bitfield_t; } + +std::unique_ptr cpp_bitfield::build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, std::unique_ptr type, + unsigned no_bits, bool is_mutable) +{ + auto result = std::unique_ptr( + new cpp_bitfield(std::move(name), std::move(type), no_bits, is_mutable)); + idx.register_entity(std::move(id), type_safe::cref(*result)); + return result; +} + +std::unique_ptr cpp_bitfield::build(std::unique_ptr type, unsigned no_bits, + bool is_mutable) +{ + return std::unique_ptr( + new cpp_bitfield("", std::move(type), no_bits, is_mutable)); +} + +cpp_entity_kind cpp_bitfield::do_get_entity_kind() const noexcept +{ + return kind(); +} diff --git a/src/libclang/expression_parser.cpp b/src/libclang/expression_parser.cpp index 17ef82d..b9bb537 100644 --- a/src/libclang/expression_parser.cpp +++ b/src/libclang/expression_parser.cpp @@ -8,6 +8,18 @@ using namespace cppast; +namespace +{ + std::string get_expression_str(detail::token_stream& stream) + { + // just concat everything + std::string expr; + while (!stream.done()) + expr += stream.get().c_str(); + return expr; + } +} + std::unique_ptr detail::parse_expression(const detail::parse_context& context, const CXCursor& cur) { @@ -17,12 +29,8 @@ std::unique_ptr detail::parse_expression(const detail::parse_con detail::tokenizer tokenizer(context.tu, context.file, cur); detail::token_stream stream(tokenizer, cur); - // just concat everything - std::string expr; - while (!stream.done()) - expr += stream.get().c_str(); - auto type = parse_type(context, clang_getCursorType(cur)); + auto expr = get_expression_str(stream); if (kind == CXCursor_CharacterLiteral || kind == CXCursor_CompoundLiteralExpr || kind == CXCursor_FloatingLiteral || kind == CXCursor_ImaginaryLiteral @@ -32,3 +40,13 @@ std::unique_ptr detail::parse_expression(const detail::parse_con else return cpp_unexposed_expression::build(std::move(type), std::move(expr)); } + +std::unique_ptr detail::parse_raw_expression(const parse_context& context, + token_stream& stream, + const CXType& type) +{ + if (stream.done()) + return nullptr; + auto expr = get_expression_str(stream); + return cpp_unexposed_expression::build(parse_type(context, type), std::move(expr)); +} diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 4552442..15f5e40 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -51,6 +51,8 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_VarDecl: return parse_cpp_variable(context, cur); + case CXCursor_FieldDecl: + return parse_cpp_member_variable(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 31dede8..b1f78fb 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -38,6 +38,13 @@ namespace cppast std::unique_ptr parse_expression(const parse_context& context, const CXCursor& cur); + // parse the expression starting at the current token in the stream + // and ends at the end of the stream + // this is required for situations where there is no expression cursor exposed, + // like member initializers + std::unique_ptr parse_raw_expression(const parse_context& context, + token_stream& stream, + const CXType& type); // parse_entity() dispatches on the cursor type // it calls one of the other parse functions defined elsewhere @@ -65,6 +72,9 @@ namespace cppast std::unique_ptr parse_cpp_variable(const parse_context& context, const CXCursor& cur); + // also parses bitfields + std::unique_ptr parse_cpp_member_variable(const parse_context& context, + const CXCursor& cur); std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur); } diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index f913f6d..5a62d7c 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -143,7 +143,8 @@ namespace } } else if (clang_isExpression(clang_getCursorKind(cur)) - || clang_getCursorKind(cur) == CXCursor_CXXBaseSpecifier) + || clang_getCursorKind(cur) == CXCursor_CXXBaseSpecifier + || clang_getCursorKind(cur) == CXCursor_FieldDecl) // need to shrink range by one end = get_next_location(tu, file, end, -1); diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index 8e32f18..0846ed4 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -4,6 +4,7 @@ #include "parse_functions.hpp" +#include #include #include @@ -80,3 +81,38 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte return cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), std::move(default_value), storage_class, is_constexpr); } + +std::unique_ptr detail::parse_cpp_member_variable(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_FieldDecl, detail::assert_handler{}); + + auto name = get_cursor_name(cur); + auto type = parse_type(context, clang_getCursorType(cur)); + auto is_mutable = clang_CXXField_isMutable(cur) != 0u; + + if (clang_Cursor_isBitField(cur)) + { + auto no_bits = clang_getFieldDeclBitWidth(cur); + DEBUG_ASSERT(no_bits >= 0, detail::parse_error_handler{}, cur, "invalid number of bits"); + if (name.empty()) + return cpp_bitfield::build(std::move(type), unsigned(no_bits), is_mutable); + else + return cpp_bitfield::build(*context.idx, get_entity_id(cur), name.c_str(), + std::move(type), unsigned(no_bits), is_mutable); + } + else + { + // parse_default_value() doesn't work here + tokenizer tokenizer(context.tu, context.file, cur); + token_stream stream(tokenizer, cur); + + // look for the equal sign, default value starts there + while (!stream.done() && !skip_if(stream, "=")) + stream.bump(); + auto default_value = parse_raw_expression(context, stream, clang_getCursorType(cur)); + + return cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(), + std::move(type), std::move(default_value), is_mutable); + } +} diff --git a/test/cpp_member_variable.cpp b/test/cpp_member_variable.cpp new file mode 100644 index 0000000..e6d9520 --- /dev/null +++ b/test/cpp_member_variable.cpp @@ -0,0 +1,103 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_member_variable") +{ + auto code = R"( +struct foo +{ + int a; + float b = 3.14f; + mutable char c; +}; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_member_variable.cpp", code); + auto count = test_visit(*file, [&](const cpp_member_variable& var) { + if (var.name() == "a") + { + auto type = cpp_builtin_type::build("int"); + REQUIRE(equal_types(idx, var.type(), *type)); + REQUIRE(!var.default_value()); + REQUIRE(!var.is_mutable()); + } + else if (var.name() == "b") + { + auto type = cpp_builtin_type::build("float"); + REQUIRE(equal_types(idx, var.type(), *type)); + + // all initializers are unexposed + auto def = cpp_unexposed_expression::build(cpp_builtin_type::build("float"), "3.14f"); + REQUIRE(var.default_value()); + REQUIRE(equal_expressions(var.default_value().value(), *def)); + + REQUIRE(!var.is_mutable()); + } + else if (var.name() == "c") + { + auto type = cpp_builtin_type::build("char"); + REQUIRE(equal_types(idx, var.type(), *type)); + REQUIRE(!var.default_value()); + REQUIRE(var.is_mutable()); + } + else + REQUIRE(false); + }); + REQUIRE(count == 3u); +} + +TEST_CASE("cpp_bitfield") +{ + auto code = R"( +struct foo +{ + char a : 3; + mutable char b : 2; + char : 0; + char c : 3; + char : 4; +}; +)"; + + auto file = parse({}, "cpp_bitfield.cpp", code); + auto count = test_visit(*file, [&](const cpp_bitfield& bf) { + REQUIRE(!bf.default_value()); + REQUIRE(equal_types({}, bf.type(), *cpp_builtin_type::build("char"))); + + if (bf.name() == "a") + { + REQUIRE(bf.no_bits() == 3u); + REQUIRE(!bf.is_mutable()); + } + else if (bf.name() == "b") + { + REQUIRE(bf.no_bits() == 2u); + REQUIRE(bf.is_mutable()); + } + else if (bf.name() == "c") + { + REQUIRE(bf.no_bits() == 3u); + REQUIRE(!bf.is_mutable()); + } + else if (bf.name() == "") + { + REQUIRE(!bf.is_mutable()); + if (bf.no_bits() != 0u && bf.no_bits() != 4u) + { + INFO(bf.no_bits()); + REQUIRE(false); + } + } + else + REQUIRE(false); + }); + REQUIRE(count == 5u); +}