Parse cpp_using_directive/declaration

Both of them now also don't have a name() anymore as it was inconsistent with get_full_name().
This commit is contained in:
Jonathan Müller 2017-02-22 20:09:40 +01:00
commit 087b9583ff
7 changed files with 176 additions and 22 deletions

View file

@ -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<cpp_using_directive> build(const cpp_namespace_ref& target)
static std::unique_ptr<cpp_using_directive> build(cpp_namespace_ref target)
{
return std::unique_ptr<cpp_using_directive>(new cpp_using_directive(target));
return std::unique_ptr<cpp_using_directive>(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<cpp_using_declaration> build(const cpp_entity_ref& target)
static std::unique_ptr<cpp_using_declaration> build(cpp_entity_ref target)
{
return std::unique_ptr<cpp_using_declaration>(new cpp_using_declaration(target));
return std::unique_ptr<cpp_using_declaration>(
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

View file

@ -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())

View file

@ -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();
}

View file

@ -5,6 +5,7 @@
#include "parse_functions.hpp"
#include <cppast/cpp_namespace.hpp>
#include <clang-c/Index.h>
#include "libclang_visitor.hpp"
@ -50,7 +51,7 @@ std::unique_ptr<cpp_entity> 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<cpp_entity> 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<cpp_entity> 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 <nested identifier>;
detail::skip(stream, "using");
detail::skip(stream, "namespace");
// <nested identifier>;
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<cpp_entity> 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 <nested identifier>;
detail::skip(stream, "using");
// <nested identifier>;
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));
}

View file

@ -23,6 +23,10 @@ std::unique_ptr<cpp_entity> 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;

View file

@ -28,9 +28,12 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_declaration(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
}

View file

@ -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<cpp_using_directive>(*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
}