From 45e9e5305bfd5bf391eb0dedfcbfb01eb06686ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Wed, 22 Feb 2017 11:04:34 +0100 Subject: [PATCH] Parse cpp_namespace_alias --- include/cppast/cpp_entity_ref.hpp | 11 ++++++ include/cppast/cpp_namespace.hpp | 4 +++ src/cpp_entity_ref.cpp | 15 ++++++++ src/cpp_namespace.cpp | 7 +++- src/libclang/libclang_visitor.hpp | 21 ++++++----- src/libclang/namespace_parser.cpp | 51 +++++++++++++++++++++++++-- src/libclang/parse_functions.cpp | 2 ++ src/libclang/parse_functions.hpp | 3 ++ test/cpp_namespace.cpp | 58 +++++++++++++++++++++++++++++++ 9 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 src/cpp_entity_ref.cpp diff --git a/include/cppast/cpp_entity_ref.hpp b/include/cppast/cpp_entity_ref.hpp index d99def9..3c78170 100644 --- a/include/cppast/cpp_entity_ref.hpp +++ b/include/cppast/cpp_entity_ref.hpp @@ -9,6 +9,14 @@ namespace cppast { + enum class cpp_entity_kind; + + /// \exclude + namespace detail + { + void check_entity_cast(const cpp_entity& e, cpp_entity_kind expected_kind); + } // namespace detail + /// A basic reference to some kind of [cppast::cpp_entity](). template class basic_cpp_entity_ref @@ -36,6 +44,9 @@ namespace cppast type_safe::optional_ref get(const cpp_entity_index& idx) const noexcept { auto entity = idx.lookup(target_); + if (!entity) + return type_safe::nullopt; + detail::check_entity_cast(entity.value(), T::kind()); return static_cast(entity.value()); } diff --git a/include/cppast/cpp_namespace.hpp b/include/cppast/cpp_namespace.hpp index a7ea8a8..97142ac 100644 --- a/include/cppast/cpp_namespace.hpp +++ b/include/cppast/cpp_namespace.hpp @@ -88,12 +88,16 @@ namespace cppast class cpp_namespace_alias final : public cpp_entity { public: + static cpp_entity_kind kind() noexcept; + /// \returns A newly created and registered namespace alias. static std::unique_ptr build(const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_namespace_ref target); /// \returns The [cppast::cpp_namespace_ref]() to the aliased namespace. + /// \notes If the namespace aliases aliases another namespace alias, + /// the target entity will still be the namespace, not the alias. const cpp_namespace_ref& target() const noexcept { return target_; diff --git a/src/cpp_entity_ref.cpp b/src/cpp_entity_ref.cpp new file mode 100644 index 0000000..1dd27de --- /dev/null +++ b/src/cpp_entity_ref.cpp @@ -0,0 +1,15 @@ +// 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 + +using namespace cppast; + +void detail::check_entity_cast(const cpp_entity& e, cpp_entity_kind expected_kind) +{ + DEBUG_ASSERT(e.kind() == expected_kind, detail::precondition_error_handler{}, + "mismatched entity kind"); +} diff --git a/src/cpp_namespace.cpp b/src/cpp_namespace.cpp index 319525a..8821733 100644 --- a/src/cpp_namespace.cpp +++ b/src/cpp_namespace.cpp @@ -33,11 +33,16 @@ std::unique_ptr cpp_namespace_alias::build(const cpp_entity return ptr; } -cpp_entity_kind cpp_namespace_alias::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_namespace_alias::kind() noexcept { return cpp_entity_kind::namespace_alias_t; } +cpp_entity_kind cpp_namespace_alias::do_get_entity_kind() const noexcept +{ + return kind(); +} + cpp_entity_kind cpp_using_directive::do_get_entity_kind() const noexcept { return cpp_entity_kind::using_directive_t; diff --git a/src/libclang/libclang_visitor.hpp b/src/libclang/libclang_visitor.hpp index bc11481..431ec09 100644 --- a/src/libclang/libclang_visitor.hpp +++ b/src/libclang/libclang_visitor.hpp @@ -15,15 +15,20 @@ namespace cppast { // visits direct children of an entity template - void visit_children(CXCursor parent, Func f) + void visit_children(CXCursor parent, Func f, bool recurse = false) { - clang_visitChildren(parent, - [](CXCursor cur, CXCursor, CXClientData data) { - auto& actual_cb = *static_cast(data); - actual_cb(cur); - return CXChildVisit_Continue; - }, - &f); + auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) { + auto& actual_cb = *static_cast(data); + actual_cb(cur); + return CXChildVisit_Continue; + }; + auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) { + auto& actual_cb = *static_cast(data); + actual_cb(cur); + return CXChildVisit_Recurse; + }; + + clang_visitChildren(parent, recurse ? recurse_lambda : continue_lambda, &f); } // visits a translation unit diff --git a/src/libclang/namespace_parser.cpp b/src/libclang/namespace_parser.cpp index c7cee09..8976b26 100644 --- a/src/libclang/namespace_parser.cpp +++ b/src/libclang/namespace_parser.cpp @@ -12,7 +12,8 @@ using namespace cppast; namespace { - cpp_namespace::builder make_builder(const detail::parse_context& context, const CXCursor& cur) + cpp_namespace::builder make_ns_builder(const detail::parse_context& context, + const CXCursor& cur) { detail::tokenizer tokenizer(context.tu, context.file, cur); detail::token_stream stream(tokenizer, cur); @@ -38,7 +39,7 @@ std::unique_ptr detail::parse_cpp_namespace(const detail::parse_cont { DEBUG_ASSERT(cur.kind == CXCursor_Namespace, detail::assert_handler{}); - auto builder = make_builder(context, cur); + auto builder = make_ns_builder(context, cur); detail::visit_children(cur, [&](const CXCursor& cur) { auto entity = parse_entity(context, cur); if (entity) @@ -46,3 +47,49 @@ std::unique_ptr detail::parse_cpp_namespace(const detail::parse_cont }); return builder.finish(*context.idx, get_entity_id(cur)); } + +namespace +{ + cpp_entity_id ns_alias_target_id(const CXCursor& cur) + { + cpp_entity_id result(""); + detail::visit_children(cur, + [&](const CXCursor& child) { + auto referenced = clang_getCursorReferenced(child); + auto kind = clang_getCursorKind(referenced); + if (kind == CXCursor_Namespace) + result = detail::get_entity_id(referenced); + else if (kind == CXCursor_NamespaceAlias) + // get target of namespace alias instead + result = ns_alias_target_id(referenced); + else + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unexpected child of namespace alias"); + }, + true); + return result; + } +} + +std::unique_ptr detail::parse_cpp_namespace_alias(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_NamespaceAlias, detail::assert_handler{}); + + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + + // namespace = ; + detail::skip(stream, "namespace"); + auto name = stream.get().c_str(); + detail::skip(stream, "="); + + // ; + std::string target_name; + while (!detail::skip_if(stream, ";")) + target_name += stream.get().c_str(); + + auto target = cpp_namespace_ref(ns_alias_target_id(cur), std::move(target_name)); + return cpp_namespace_alias::build(*context.idx, get_entity_id(cur), std::move(name), + std::move(target)); +} diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index d518662..573c015 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -21,6 +21,8 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co { case CXCursor_Namespace: return parse_cpp_namespace(context, cur); + case CXCursor_NamespaceAlias: + return parse_cpp_namespace_alias(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 192a0c7..0860de8 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -29,6 +29,9 @@ namespace cppast std::unique_ptr parse_cpp_namespace(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_namespace_alias(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/test/cpp_namespace.cpp b/test/cpp_namespace.cpp index f16c445..be58169 100644 --- a/test/cpp_namespace.cpp +++ b/test/cpp_namespace.cpp @@ -50,3 +50,61 @@ namespace c }); REQUIRE(count == 4u); } + +TEST_CASE("cpp_namespace_alias") +{ + auto code = R"( +namespace ns1 {} +namespace ns2 {} + +namespace a = ns1; +namespace b = ns2; + +namespace outer +{ + namespace ns {} + + namespace c = ns; + namespace d = ns1; +} + +namespace e = outer::ns; +namespace f = outer::c; +)"; + + cpp_entity_index idx; + auto check_alias = [&](const cpp_namespace_alias& alias, const char* target_name, + const char* target_full_name) { + auto& target = alias.target(); + REQUIRE(target.name() == target_name); + + auto entity = target.get(idx); + REQUIRE(entity); + REQUIRE(full_name(entity.value()) == target_full_name); + }; + + auto file = parse(idx, "cpp_namespace_alias.cpp", code); + auto count = test_visit(*file, [&](const cpp_namespace_alias& alias) { + if (alias.name() == "a") + check_alias(alias, "ns1", "ns1"); + else if (alias.name() == "b") + check_alias(alias, "ns2", "ns2"); + else if (alias.name() == "c") + { + check_parent(alias, "outer", "outer::c"); + check_alias(alias, "ns", "outer::ns"); + } + else if (alias.name() == "d") + { + check_parent(alias, "outer", "outer::d"); + check_alias(alias, "ns1", "ns1"); + } + else if (alias.name() == "e") + check_alias(alias, "outer::ns", "outer::ns"); + else if (alias.name() == "f") + check_alias(alias, "outer::c", "outer::ns"); + else + REQUIRE(false); + }); + REQUIRE(count == 6u); +}