From 51bdbca81dd710e40f20084912fcb200a75fcf0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Fri, 24 Feb 2017 22:29:48 +0100 Subject: [PATCH] Parse cpp_variable --- include/cppast/cpp_function.hpp | 16 ++-- .../cppast/cpp_storage_class_specifiers.hpp | 53 ++++++++++++ include/cppast/cpp_storage_specifiers.hpp | 40 --------- include/cppast/cpp_variable.hpp | 15 ++-- src/cpp_variable.cpp | 10 ++- src/libclang/class_parser.cpp | 2 +- src/libclang/parse_functions.cpp | 8 ++ src/libclang/parse_functions.hpp | 8 ++ src/libclang/type_parser.cpp | 2 +- src/libclang/variable_parser.cpp | 82 +++++++++++++++++++ test/cpp_variable.cpp | 81 ++++++++++++++++++ test/test_parser.hpp | 22 +++++ 12 files changed, 281 insertions(+), 58 deletions(-) create mode 100644 include/cppast/cpp_storage_class_specifiers.hpp delete mode 100644 include/cppast/cpp_storage_specifiers.hpp create mode 100644 src/libclang/variable_parser.cpp create mode 100644 test/cpp_variable.cpp diff --git a/include/cppast/cpp_function.hpp b/include/cppast/cpp_function.hpp index 1ebc10d..66a36c0 100644 --- a/include/cppast/cpp_function.hpp +++ b/include/cppast/cpp_function.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include namespace cppast @@ -157,7 +157,7 @@ namespace cppast } /// \effects Sets the storage class. - void storage_class(cpp_storage_specifiers storage) + void storage_class(cpp_storage_class_specifiers storage) { function->storage_ = storage; } @@ -184,7 +184,7 @@ namespace cppast /// \returns The [cppast::cpp_storage_specifiers]() of the function. /// \notes If it is `cpp_storage_class_static` and inside a [cppast::cpp_class](), /// it is a `static` class function. - cpp_storage_specifiers storage_class() const noexcept + cpp_storage_class_specifiers storage_class() const noexcept { return storage_; } @@ -207,16 +207,16 @@ namespace cppast cpp_function(std::string name, std::unique_ptr ret) : cpp_function_base(std::move(name)), return_type_(std::move(ret)), - storage_(cpp_storage_class_none), + storage_(cpp_storage_class_auto), friend_(false), constexpr_(false) { } - std::unique_ptr return_type_; - cpp_storage_specifiers storage_; - bool friend_; - bool constexpr_; + std::unique_ptr return_type_; + cpp_storage_class_specifiers storage_; + bool friend_; + bool constexpr_; }; } // namespace cppast diff --git a/include/cppast/cpp_storage_class_specifiers.hpp b/include/cppast/cpp_storage_class_specifiers.hpp new file mode 100644 index 0000000..f7993e3 --- /dev/null +++ b/include/cppast/cpp_storage_class_specifiers.hpp @@ -0,0 +1,53 @@ +// 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. + +#ifndef CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED +#define CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED + +namespace cppast +{ + /// C++ storage class specifiers. + /// + /// See http://en.cppreference.com/w/cpp/language/storage_duration, for example. + enum cpp_storage_class_specifiers + { + cpp_storage_class_none = 0, //< no storage class specifier given. + + cpp_storage_class_auto = 1, //< *automatic* storage duration. + + cpp_storage_class_static = + 2, //< *static* or *thread* storage duration and *internal* linkage. + cpp_storage_class_extern = + 4, //< *static* or *thread* storage duration and *external* linkage. + + cpp_storage_class_thread_local = 8, //< *thread* storage duration. + /// \notes This is the only one that can be combined with the others. + }; + + /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`. + inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept + { + return (spec & cpp_storage_class_thread_local) != 0; + } + + /// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`. + inline bool is_auto(cpp_storage_class_specifiers spec) noexcept + { + return (spec & cpp_storage_class_auto) != 0; + } + + /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`. + inline bool is_static(cpp_storage_class_specifiers spec) noexcept + { + return (spec & cpp_storage_class_static) != 0; + } + + /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`. + inline bool is_extern(cpp_storage_class_specifiers spec) noexcept + { + return (spec & cpp_storage_class_extern) != 0; + } +} // namespace cppast + +#endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED diff --git a/include/cppast/cpp_storage_specifiers.hpp b/include/cppast/cpp_storage_specifiers.hpp deleted file mode 100644 index fee9171..0000000 --- a/include/cppast/cpp_storage_specifiers.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// 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. - -#ifndef CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED -#define CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED - -namespace cppast -{ - /// C++ storage class specifiers. - enum cpp_storage_specifiers - { - cpp_storage_class_none = 0, - - cpp_storage_class_static = 1, - cpp_storage_class_extern = 2, - - cpp_storage_class_thread_local = 4, - }; - - /// \returns Whether the [cppast::cpp_storage_specifiers]() contain `thread_local`. - inline bool is_thread_local(cpp_storage_specifiers spec) noexcept - { - return (spec & cpp_storage_class_thread_local) != 0; - } - - /// \returns Whether the [cppast::cpp_storage_specifiers]() contain `static`. - inline bool is_static(cpp_storage_specifiers spec) noexcept - { - return (spec & cpp_storage_class_static) != 0; - } - - /// \returns Whether the [cppast::cpp_storage_specifiers]() contain `extern`. - inline bool is_extern(cpp_storage_specifiers spec) noexcept - { - return (spec & cpp_storage_class_extern) != 0; - } -} // namespace cppast - -#endif // CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED diff --git a/include/cppast/cpp_variable.hpp b/include/cppast/cpp_variable.hpp index 631cb95..3851f9c 100644 --- a/include/cppast/cpp_variable.hpp +++ b/include/cppast/cpp_variable.hpp @@ -6,7 +6,7 @@ #define CPPAST_CPP_VARIABLE_HPP_INCLUDED #include -#include +#include #include namespace cppast @@ -17,15 +17,18 @@ namespace cppast class cpp_variable final : public cpp_entity, public cpp_variable_base { public: + static cpp_entity_kind kind() noexcept; + /// \returns A newly created and registered variable. /// \notes The default value may be `nullptr` indicating no default value. 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, - cpp_storage_specifiers spec, bool is_constexpr); + cpp_storage_class_specifiers spec, + bool is_constexpr); /// \returns The [cppast::cpp_storage_specifiers]() on that variable. - cpp_storage_specifiers storage_class() const noexcept + cpp_storage_class_specifiers storage_class() const noexcept { return storage_; } @@ -38,7 +41,7 @@ namespace cppast private: cpp_variable(std::string name, std::unique_ptr type, - std::unique_ptr def, cpp_storage_specifiers spec, + std::unique_ptr def, cpp_storage_class_specifiers spec, bool is_constexpr) : cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def)), @@ -49,8 +52,8 @@ namespace cppast cpp_entity_kind do_get_entity_kind() const noexcept override; - cpp_storage_specifiers storage_; - bool is_constexpr_; + cpp_storage_class_specifiers storage_; + bool is_constexpr_; }; } // namespace cppast diff --git a/src/cpp_variable.cpp b/src/cpp_variable.cpp index c084362..58b918e 100644 --- a/src/cpp_variable.cpp +++ b/src/cpp_variable.cpp @@ -8,10 +8,16 @@ using namespace cppast; +cpp_entity_kind cpp_variable::kind() noexcept +{ + return cpp_entity_kind::variable_t; +} + std::unique_ptr cpp_variable::build(const cpp_entity_index& idx, cpp_entity_id id, std::string name, std::unique_ptr type, std::unique_ptr def, - cpp_storage_specifiers spec, bool is_constexpr) + cpp_storage_class_specifiers spec, + bool is_constexpr) { auto result = std::unique_ptr( new cpp_variable(std::move(name), std::move(type), std::move(def), spec, is_constexpr)); @@ -21,5 +27,5 @@ std::unique_ptr cpp_variable::build(const cpp_entity_index& idx, c cpp_entity_kind cpp_variable::do_get_entity_kind() const noexcept { - return cpp_entity_kind::variable_t; + return kind(); } diff --git a/src/libclang/class_parser.cpp b/src/libclang/class_parser.cpp index 3bd13f7..148e676 100644 --- a/src/libclang/class_parser.cpp +++ b/src/libclang/class_parser.cpp @@ -32,7 +32,7 @@ namespace cpp_class::builder make_class_builder(const CXCursor& cur) { auto kind = parse_class_kind(cur); - auto name = detail::cxstring(clang_getCursorSpelling(cur)); + auto name = detail::get_cursor_name(cur); return cpp_class::builder(name.c_str(), kind); } diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 4b6080e..4552442 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -13,6 +13,11 @@ cpp_entity_id detail::get_entity_id(const CXCursor& cur) return cpp_entity_id(usr.c_str()); } +detail::cxstring detail::get_cursor_name(const CXCursor& cur) +{ + return cxstring(clang_getCursorSpelling(cur)); +} + std::unique_ptr detail::parse_entity(const detail::parse_context& context, const CXCursor& cur) try { @@ -44,6 +49,9 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_UnionDecl: return parse_cpp_class(context, cur); + case CXCursor_VarDecl: + return parse_cpp_variable(context, cur); + default: break; } diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 080772f..31dede8 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -21,6 +21,11 @@ namespace cppast { cpp_entity_id get_entity_id(const CXCursor& cur); + // only use this if the name is just a single token + // never where it is a reference to something (like base class name) + // as then you won't get it "as-is" + cxstring get_cursor_name(const CXCursor& cur); + struct parse_context { CXTranslationUnit tu; @@ -58,6 +63,9 @@ namespace cppast std::unique_ptr parse_cpp_class(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_variable(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur); } } // namespace cppast::detail diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index af6558c..3d1698d 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -412,7 +412,7 @@ std::unique_ptr detail::parse_cpp_type_alias(const detail::parse_con DEBUG_ASSERT(cur.kind == CXCursor_TypeAliasDecl || cur.kind == CXCursor_TypedefDecl, detail::assert_handler{}); - auto name = cxstring(clang_getCursorSpelling(cur)); + auto name = detail::get_cursor_name(cur); auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur)); return cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type)); } diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp new file mode 100644 index 0000000..8e32f18 --- /dev/null +++ b/src/libclang/variable_parser.cpp @@ -0,0 +1,82 @@ +// 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 "parse_functions.hpp" + +#include +#include + +#include "libclang_visitor.hpp" + +using namespace cppast; + +namespace +{ + std::unique_ptr parse_default_value(const detail::parse_context& context, + const CXCursor& cur) + { + std::unique_ptr expression; + detail::visit_children(cur, [&](const CXCursor& child) { + DEBUG_ASSERT(clang_isExpression(child.kind) && !expression, + detail::parse_error_handler{}, cur, "unexpected child cursor of variable"); + + expression = detail::parse_expression(context, child); + }); + return expression; + } + + cpp_storage_class_specifiers parse_storage_class(const CXCursor& cur) + { + switch (clang_Cursor_getStorageClass(cur)) + { + case CX_SC_Invalid: + break; + + case CX_SC_None: + return cpp_storage_class_none; + + case CX_SC_Auto: + case CX_SC_Register: + return cpp_storage_class_auto; + + case CX_SC_Extern: + return cpp_storage_class_extern; + case CX_SC_Static: + return cpp_storage_class_static; + + case CX_SC_PrivateExtern: + case CX_SC_OpenCLWorkGroupLocal: + // non-exposed storage classes + return cpp_storage_class_auto; + } + + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class"); + return cpp_storage_class_auto; + } +} + +std::unique_ptr detail::parse_cpp_variable(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_VarDecl, detail::assert_handler{}); + + auto name = get_cursor_name(cur); + auto type = parse_type(context, clang_getCursorType(cur)); + auto default_value = parse_default_value(context, cur); + auto storage_class = parse_storage_class(cur); + auto is_constexpr = false; + + // just look for thread local or constexpr + // can't appear anywhere else, so good enough + detail::tokenizer tokenizer(context.tu, context.file, cur); + for (auto& token : tokenizer) + if (token.value() == "thread_local") + storage_class = + cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); + else if (token.value() == "constexpr") + is_constexpr = true; + + return cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), + std::move(default_value), storage_class, is_constexpr); +} diff --git a/test/cpp_variable.cpp b/test/cpp_variable.cpp new file mode 100644 index 0000000..c4bb11f --- /dev/null +++ b/test/cpp_variable.cpp @@ -0,0 +1,81 @@ +// 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_variable") +{ + auto code = R"( +// basic - global scope, so external storage +int a; +unsigned long long b = 42; +float c = 3.f + 0.14f; + +// with storage class specifiers +extern int d; // actually declaration +static int e; +thread_local int f; +thread_local static int g; + +// constexpr +constexpr int h = 12; +)"; + + cpp_entity_index idx; + auto check_variable = [&](const cpp_variable& var, const cpp_type& type, + type_safe::optional_ref default_value, + int storage_class, bool is_constexpr) { + REQUIRE(equal_types(idx, var.type(), type)); + if (var.default_value()) + { + REQUIRE(default_value); + REQUIRE(equal_expressions(var.default_value().value(), default_value.value())); + } + else + REQUIRE(!default_value); + + REQUIRE(var.storage_class() == storage_class); + REQUIRE(var.is_constexpr() == is_constexpr); + }; + + auto file = parse({}, "cpp_variable.cpp", code); + + auto int_type = cpp_builtin_type::build("int"); + auto count = test_visit(*file, [&](const cpp_variable& var) { + INFO(var.name()); + if (var.name() == "a") + check_variable(var, *int_type, nullptr, cpp_storage_class_none, false); + else if (var.name() == "b") + check_variable(var, *cpp_builtin_type::build("unsigned long long"), + // unexposed due to implicit cast, I think + *cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "42"), + cpp_storage_class_none, false); + else if (var.name() == "c") + check_variable(var, *cpp_builtin_type::build("float"), + *cpp_unexposed_expression::build(cpp_builtin_type::build("float"), + "3.f+0.14f"), + cpp_storage_class_none, false); + else if (var.name() == "d") + check_variable(var, *int_type, nullptr, cpp_storage_class_extern, false); + else if (var.name() == "e") + check_variable(var, *int_type, nullptr, cpp_storage_class_static, false); + else if (var.name() == "f") + check_variable(var, *int_type, nullptr, cpp_storage_class_thread_local, false); + else if (var.name() == "g") + check_variable(var, *int_type, nullptr, + cpp_storage_class_static | cpp_storage_class_thread_local, false); + else if (var.name() == "h") + check_variable(var, *cpp_cv_qualified_type::build(cpp_builtin_type::build("int"), + cpp_cv_const), + *cpp_literal_expression::build(cpp_builtin_type::build("int"), "12"), + cpp_storage_class_none, true); + else + REQUIRE(false); + }); + REQUIRE(count == 8u); +} diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 53219cd..4158a59 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -75,4 +76,25 @@ inline void check_parent(const cppast::cpp_entity& e, const char* parent_name, bool equal_types(const cppast::cpp_entity_index& idx, const cppast::cpp_type& parsed, const cppast::cpp_type& synthesized); +inline bool equal_expressions(const cppast::cpp_expression& parsed, + const cppast::cpp_expression& synthesized) +{ + using namespace cppast; + + if (parsed.kind() != synthesized.kind()) + return false; + switch (parsed.kind()) + { + case cpp_expression_kind::unexposed: + return static_cast(parsed).expression() + == static_cast(synthesized).expression(); + + case cpp_expression_kind::literal: + return static_cast(parsed).value() + == static_cast(synthesized).value(); + } + + return false; +} + #endif // CPPAST_TEST_PARSER_HPP_INCLUDED