diff --git a/include/cppast/cpp_namespace.hpp b/include/cppast/cpp_namespace.hpp index 97142ac..1b5025b 100644 --- a/include/cppast/cpp_namespace.hpp +++ b/include/cppast/cpp_namespace.hpp @@ -117,65 +117,69 @@ namespace cppast /// A [cppast::cpp_entity]() modelling a using directive. /// /// A using directive is `using namespace std`, for example. + /// \notes It does not have a name. class cpp_using_directive final : public cpp_entity { public: + static cpp_entity_kind kind() noexcept; + /// \returns A newly created using directive. /// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), /// as nothing can refer to it. - static std::unique_ptr build(const cpp_namespace_ref& target) + static std::unique_ptr build(cpp_namespace_ref target) { - return std::unique_ptr(new cpp_using_directive(target)); + return std::unique_ptr(new cpp_using_directive(std::move(target))); } /// \returns The [cppast::cpp_namespace_ref]() that is being used. - /// \notes The name of the reference is the same as the name of this entity. - cpp_namespace_ref target() const + const cpp_namespace_ref& target() const { - return {target_, name()}; + return target_; } private: - cpp_using_directive(const cpp_namespace_ref& target) - : cpp_entity(target.name()), target_(target.id()) + cpp_using_directive(cpp_namespace_ref target) : cpp_entity(""), target_(std::move(target)) { } cpp_entity_kind do_get_entity_kind() const noexcept override; - cpp_entity_id target_; + cpp_namespace_ref target_; }; /// A [cppast::cpp_entity]() modelling a using declaration. /// /// A using declaration is `using std::vector`, for example. + /// \notes It does not have a name. class cpp_using_declaration final : public cpp_entity { public: + static cpp_entity_kind kind() noexcept; + /// \returns A newly created using declaration. /// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), /// as nothing can refer to it. - static std::unique_ptr build(const cpp_entity_ref& target) + static std::unique_ptr build(cpp_entity_ref target) { - return std::unique_ptr(new cpp_using_declaration(target)); + return std::unique_ptr( + new cpp_using_declaration(std::move(target))); } /// \returns The [cppast::cpp_entity_ref]() that is being used. /// \notes The name of the reference is the same as the name of this entity. - cpp_entity_ref target() const + const cpp_entity_ref& target() const noexcept { - return {target_, name()}; + return target_; } private: - cpp_using_declaration(const cpp_entity_ref& target) - : cpp_entity(target.name()), target_(target.id()) + cpp_using_declaration(cpp_entity_ref target) : cpp_entity(""), target_(std::move(target)) { } cpp_entity_kind do_get_entity_kind() const noexcept override; - cpp_entity_id target_; + cpp_entity_ref target_; }; } // namespace cppast diff --git a/src/cpp_entity.cpp b/src/cpp_entity.cpp index 4950ad2..3c370c5 100644 --- a/src/cpp_entity.cpp +++ b/src/cpp_entity.cpp @@ -8,6 +8,9 @@ using namespace cppast; std::string cppast::full_name(const cpp_entity& e) { + if (e.name().empty()) + return ""; + std::string scopes; for (auto cur = e.parent(); cur; cur = cur.value().parent()) diff --git a/src/cpp_namespace.cpp b/src/cpp_namespace.cpp index 8821733..caee2be 100644 --- a/src/cpp_namespace.cpp +++ b/src/cpp_namespace.cpp @@ -43,12 +43,22 @@ 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 +cpp_entity_kind cpp_using_directive::kind() noexcept { return cpp_entity_kind::using_directive_t; } -cpp_entity_kind cpp_using_declaration::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_using_directive::do_get_entity_kind() const noexcept +{ + return kind(); +} + +cpp_entity_kind cpp_using_declaration::kind() noexcept { return cpp_entity_kind::using_declaration_t; } + +cpp_entity_kind cpp_using_declaration::do_get_entity_kind() const noexcept +{ + return kind(); +} diff --git a/src/libclang/namespace_parser.cpp b/src/libclang/namespace_parser.cpp index 8976b26..3e74331 100644 --- a/src/libclang/namespace_parser.cpp +++ b/src/libclang/namespace_parser.cpp @@ -5,6 +5,7 @@ #include "parse_functions.hpp" #include +#include #include "libclang_visitor.hpp" @@ -50,7 +51,7 @@ std::unique_ptr detail::parse_cpp_namespace(const detail::parse_cont namespace { - cpp_entity_id ns_alias_target_id(const CXCursor& cur) + cpp_entity_id parse_ns_target_cursor(const CXCursor& cur) { cpp_entity_id result(""); detail::visit_children(cur, @@ -61,10 +62,11 @@ namespace result = detail::get_entity_id(referenced); else if (kind == CXCursor_NamespaceAlias) // get target of namespace alias instead - result = ns_alias_target_id(referenced); + result = parse_ns_target_cursor(referenced); else DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, - "unexpected child of namespace alias"); + "unexpected target for namespace " + "alias/using directive"); }, true); return result; @@ -89,7 +91,77 @@ std::unique_ptr detail::parse_cpp_namespace_alias(const detail::pars 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)); + auto target = cpp_namespace_ref(parse_ns_target_cursor(cur), std::move(target_name)); return cpp_namespace_alias::build(*context.idx, get_entity_id(cur), std::move(name), std::move(target)); } + +std::unique_ptr detail::parse_cpp_using_directive(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_UsingDirective, detail::assert_handler{}); + + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + + // using namespace ; + detail::skip(stream, "using"); + detail::skip(stream, "namespace"); + + // ; + std::string target_name; + while (!detail::skip_if(stream, ";")) + target_name += stream.get().c_str(); + + auto target = cpp_namespace_ref(parse_ns_target_cursor(cur), std::move(target_name)); + return cpp_using_directive::build(target); +} + +namespace +{ + cpp_entity_id parse_entity_target_cursor(const CXCursor& cur) + { + cpp_entity_id result(""); + detail::visit_children(cur, + [&](const CXCursor& child) { + switch (clang_getCursorKind(child)) + { + case CXCursor_TypeRef: + case CXCursor_TemplateRef: + case CXCursor_MemberRef: + case CXCursor_OverloadedDeclRef: + case CXCursor_VariableRef: + break; + + default: + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unexpected target for using declaration"); + } + + auto referenced = clang_getCursorReferenced(child); + result = detail::get_entity_id(referenced); + }, + true); + return result; + } +} + +std::unique_ptr detail::parse_cpp_using_declaration( + const detail::parse_context& context, const CXCursor& cur) +{ + DEBUG_ASSERT(cur.kind == CXCursor_UsingDeclaration, detail::assert_handler{}); + + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); + + // using ; + detail::skip(stream, "using"); + + // ; + std::string target_name; + while (!detail::skip_if(stream, ";")) + target_name += stream.get().c_str(); + + auto target = cpp_entity_ref(parse_entity_target_cursor(cur), std::move(target_name)); + return cpp_using_declaration::build(std::move(target)); +} diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 573c015..6be879b 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -23,6 +23,10 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co return parse_cpp_namespace(context, cur); case CXCursor_NamespaceAlias: return parse_cpp_namespace_alias(context, cur); + case CXCursor_UsingDirective: + return parse_cpp_using_directive(context, cur); + case CXCursor_UsingDeclaration: + return parse_cpp_using_declaration(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 0860de8..38bb12f 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -28,9 +28,12 @@ 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_cpp_using_directive(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_using_declaration(const parse_context& context, + const CXCursor& cur); std::unique_ptr parse_entity(const parse_context& context, const CXCursor& cur); } diff --git a/test/cpp_namespace.cpp b/test/cpp_namespace.cpp index be58169..23a030d 100644 --- a/test/cpp_namespace.cpp +++ b/test/cpp_namespace.cpp @@ -108,3 +108,61 @@ namespace f = outer::c; }); REQUIRE(count == 6u); } + +TEST_CASE("cpp_using_directive") +{ + auto code = R"( +namespace ns1 {} +namespace ns2 {} + +using namespace ns1; +using namespace ns2; + +namespace outer +{ + namespace ns {} + + using namespace ns; + using namespace ::ns1; +} + +using namespace outer::ns; +)"; + + cpp_entity_index idx; + auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) { + auto target = directive.target(); + + auto entity = target.get(idx); + REQUIRE(entity); + REQUIRE(full_name(entity.value()) == target_full_name); + }; + + auto file = parse(idx, "cpp_using_directive.cpp", code); + auto count = test_visit(*file, [&](const cpp_using_directive& directive) { + if (directive.target().name() == "ns1") + check_directive(directive, "ns1"); + else if (directive.target().name() == "ns2") + check_directive(directive, "ns2"); + else if (directive.target().name() == "ns") + { + check_parent(directive, "outer", ""); + check_directive(directive, "outer::ns"); + } + else if (directive.target().name() == "::ns1") + { + check_parent(directive, "outer", ""); + check_directive(directive, "ns1"); + } + else if (directive.target().name() == "outer::ns") + check_directive(directive, "outer::ns"); + else + REQUIRE(false); + }); + REQUIRE(count == 5u); +} + +TEST_CASE("cpp_using_declaration") +{ + // TODO: write test when actual entities can be parsed +}