From ce69b0157f8a154736434f5b9c85046482b98c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Tue, 21 Feb 2017 22:36:38 +0100 Subject: [PATCH] Parse cpp_namespace --- include/cppast/cpp_namespace.hpp | 2 ++ src/cpp_namespace.cpp | 7 +++- src/libclang/debug_helper.cpp | 2 +- src/libclang/namespace_parser.cpp | 48 ++++++++++++++++++++++++++++ src/libclang/parse_functions.cpp | 19 +++++++++-- src/libclang/parse_functions.hpp | 7 ++++ src/libclang/raii_wrapper.hpp | 5 +++ src/libclang/tokenizer.cpp | 11 +++---- src/libclang/tokenizer.hpp | 4 +-- test/cpp_namespace.cpp | 53 +++++++++++++++++++++++++++++++ test/test_parser.hpp | 18 ++++++++--- 11 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 src/libclang/namespace_parser.cpp create mode 100644 test/cpp_namespace.cpp diff --git a/include/cppast/cpp_namespace.hpp b/include/cppast/cpp_namespace.hpp index cbbe7ca..a7ea8a8 100644 --- a/include/cppast/cpp_namespace.hpp +++ b/include/cppast/cpp_namespace.hpp @@ -17,6 +17,8 @@ namespace cppast public cpp_entity_container { public: + static cpp_entity_kind kind() noexcept; + /// Builds a [cppast::cpp_namespace](). class builder { diff --git a/src/cpp_namespace.cpp b/src/cpp_namespace.cpp index 0d5904e..319525a 100644 --- a/src/cpp_namespace.cpp +++ b/src/cpp_namespace.cpp @@ -8,11 +8,16 @@ using namespace cppast; -cpp_entity_kind cpp_namespace::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_namespace::kind() noexcept { return cpp_entity_kind::namespace_t; } +cpp_entity_kind cpp_namespace::do_get_entity_kind() const noexcept +{ + return kind(); +} + bool detail::cpp_namespace_ref_predicate::operator()(const cpp_entity& e) { return e.kind() == cpp_entity_kind::namespace_t; diff --git a/src/libclang/debug_helper.cpp b/src/libclang/debug_helper.cpp index 78af7c1..5b56e56 100644 --- a/src/libclang/debug_helper.cpp +++ b/src/libclang/debug_helper.cpp @@ -32,7 +32,7 @@ void detail::print_tokens(const detail::cxtranslation_unit& tu, const CXFile& fi const CXCursor& cur) noexcept { std::lock_guard lock(mtx); - detail::tokenizer tokenizer(tu, file, cur); + detail::tokenizer tokenizer(tu.get(), file, cur); for (auto& token : tokenizer) std::printf("%s ", token.c_str()); std::puts("\n"); diff --git a/src/libclang/namespace_parser.cpp b/src/libclang/namespace_parser.cpp new file mode 100644 index 0000000..c7cee09 --- /dev/null +++ b/src/libclang/namespace_parser.cpp @@ -0,0 +1,48 @@ +// 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 "libclang_visitor.hpp" + +using namespace cppast; + +namespace +{ + cpp_namespace::builder make_builder(const detail::parse_context& context, const CXCursor& cur) + { + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + // [inline] namespace [] { + + auto is_inline = false; + if (skip_if(stream, "inline")) + is_inline = true; + + skip(stream, "namespace"); + skip_attribute(stream); + + // { + auto& name = stream.get().value(); + skip(stream, "{"); + + return cpp_namespace::builder(name.c_str(), is_inline); + } +} + +std::unique_ptr detail::parse_cpp_namespace(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_Namespace, detail::assert_handler{}); + + auto builder = make_builder(context, cur); + detail::visit_children(cur, [&](const CXCursor& cur) { + auto entity = parse_entity(context, cur); + if (entity) + builder.add_child(std::move(entity)); + }); + return builder.finish(*context.idx, get_entity_id(cur)); +} diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 4097b64..d518662 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -4,13 +4,28 @@ #include "parse_functions.hpp" -#include "parse_error.hpp" - using namespace cppast; +cpp_entity_id detail::get_entity_id(const CXCursor& cur) +{ + cxstring usr(clang_getCursorUSR(cur)); + DEBUG_ASSERT(!usr.empty(), detail::parse_error_handler{}, cur, "cannot create id for entity"); + return cpp_entity_id(usr.c_str()); +} + std::unique_ptr detail::parse_entity(const detail::parse_context& context, const CXCursor& cur) try { + auto kind = clang_getCursorKind(cur); + switch (kind) + { + case CXCursor_Namespace: + return parse_cpp_namespace(context, cur); + + default: + break; + } + return nullptr; } catch (parse_error& ex) diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 8b174b1..192a0c7 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -9,11 +9,15 @@ #include #include "raii_wrapper.hpp" +#include "tokenizer.hpp" // for convenience +#include "parse_error.hpp" // for convenience namespace cppast { namespace detail { + cpp_entity_id get_entity_id(const CXCursor& cur); + struct parse_context { CXTranslationUnit tu; @@ -22,6 +26,9 @@ namespace cppast type_safe::object_ref idx; }; + std::unique_ptr parse_cpp_namespace(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/raii_wrapper.hpp b/src/libclang/raii_wrapper.hpp index f35aa4e..b328d66 100644 --- a/src/libclang/raii_wrapper.hpp +++ b/src/libclang/raii_wrapper.hpp @@ -129,6 +129,11 @@ namespace cppast return str_ ? str_.value().length : 0u; } + bool empty() const noexcept + { + return length() == 0u; + } + private: struct string { diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index 4c52ae0..e0cbee9 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -9,8 +9,8 @@ using namespace cppast; -detail::token::token(const detail::cxtranslation_unit& tu_unit, const CXToken& token) -: value_(clang_getTokenSpelling(tu_unit.get(), token)), kind_(clang_getTokenKind(token)) +detail::token::token(const CXTranslationUnit& tu_unit, const CXToken& token) +: value_(clang_getTokenSpelling(tu_unit, token)), kind_(clang_getTokenKind(token)) { } @@ -157,12 +157,11 @@ namespace } } -detail::tokenizer::tokenizer(const detail::cxtranslation_unit& tu, const CXFile& file, - const CXCursor& cur) +detail::tokenizer::tokenizer(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur) { - auto extent = get_extent(tu.get(), file, cur); + auto extent = get_extent(tu, file, cur); - simple_tokenizer tokenizer(tu.get(), extent, cur); + simple_tokenizer tokenizer(tu, extent, cur); tokens_.reserve(tokenizer.size()); for (auto i = 0u; i != tokenizer.size(); ++i) tokens_.emplace_back(tu, tokenizer[i]); diff --git a/src/libclang/tokenizer.hpp b/src/libclang/tokenizer.hpp index d5a9c14..68c21a7 100644 --- a/src/libclang/tokenizer.hpp +++ b/src/libclang/tokenizer.hpp @@ -16,7 +16,7 @@ namespace cppast class token { public: - explicit token(const cxtranslation_unit& tu_unit, const CXToken& token); + explicit token(const CXTranslationUnit& tu_unit, const CXToken& token); const cxstring& value() const noexcept { @@ -63,7 +63,7 @@ namespace cppast class tokenizer { public: - explicit tokenizer(const cxtranslation_unit& tu, const CXFile& file, + explicit tokenizer(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur); token_iterator begin() const noexcept diff --git a/test/cpp_namespace.cpp b/test/cpp_namespace.cpp new file mode 100644 index 0000000..bb5d86a --- /dev/null +++ b/test/cpp_namespace.cpp @@ -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. + +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_namespace") +{ + auto code = R"( +namespace a {} + +inline namespace b {} + +namespace c +{ + namespace d {} +} +)"; + + auto file = parse({}, "cpp_namespace.cpp", code); + auto count = test_visit(*file, [&](const cpp_namespace& ns) { + auto no_children = count_children(ns); + if (ns.name() == "a") + { + REQUIRE(!ns.is_inline()); + REQUIRE(no_children == 0u); + } + else if (ns.name() == "b") + { + REQUIRE(ns.is_inline()); + REQUIRE(no_children == 0u); + } + else if (ns.name() == "c") + { + REQUIRE(!ns.is_inline()); + REQUIRE(no_children == 1u); + } + else if (ns.name() == "d") + { + REQUIRE(ns.parent()); + REQUIRE(ns.parent().value().name() == "c"); + REQUIRE(!ns.is_inline()); + REQUIRE(no_children == 0u); + } + else + REQUIRE(false); + }); + REQUIRE(count == 4u); +} diff --git a/test/test_parser.hpp b/test/test_parser.hpp index b616266..20df54d 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -13,14 +13,14 @@ #include #include -void write_file(const char* name, const char* code) +inline void write_file(const char* name, const char* code) { std::ofstream file(name); file << code; } -std::unique_ptr parse(const cppast::cpp_entity_index& idx, const char* name, - const char* code) +inline std::unique_ptr parse(const cppast::cpp_entity_index& idx, + const char* name, const char* code) { using namespace cppast; @@ -38,7 +38,10 @@ template unsigned test_visit(const cppast::cpp_file& file, Func f) { auto count = 0u; - cppast::visit(file, [&](const cppast::cpp_entity& e, cppast::visitor_info) { + cppast::visit(file, [&](const cppast::cpp_entity& e, cppast::visitor_info info) { + if (info == cppast::visitor_info::container_entity_exit) + return true; // already handled + if (e.kind() == T::kind()) { auto& obj = static_cast(e); @@ -52,4 +55,11 @@ unsigned test_visit(const cppast::cpp_file& file, Func f) return count; } +// number of direct children +template +unsigned count_children(const Entity& cont) +{ + return std::distance(cont.begin(), cont.end()); +} + #endif // CPPAST_TEST_PARSER_HPP_INCLUDED