Add support for overloaded entity references

This commit is contained in:
Jonathan Müller 2017-03-19 16:07:54 +01:00
commit 08717b2ae7
8 changed files with 197 additions and 47 deletions

View file

@ -101,8 +101,9 @@ namespace cppast
private:
cpp_base_class(const cpp_type_ref& base, cpp_access_specifier_kind access, bool is_virtual)
: cpp_entity(base.name()), base_id_(base.id()), access_(access), virtual_(is_virtual)
: cpp_entity(base.name()), base_id_(base.id()[0u]), access_(access), virtual_(is_virtual)
{
DEBUG_ASSERT(!base.is_overloaded(), detail::precondition_error_handler{});
}
cpp_entity_kind do_get_entity_kind() const noexcept override;

View file

@ -5,6 +5,10 @@
#ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
#define CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
#include <vector>
#include <type_safe/variant.hpp>
#include <cppast/cpp_entity_index.hpp>
namespace cppast
@ -12,6 +16,10 @@ namespace cppast
enum class cpp_entity_kind;
/// A basic reference to some kind of [cppast::cpp_entity]().
///
/// It can either refer to a single [cppast::cpp_entity]()
/// or multiple.
/// In the later case it is *overloaded*.
template <typename T, typename Predicate>
class basic_cpp_entity_ref
{
@ -22,32 +30,152 @@ namespace cppast
{
}
/// \effects Creates it giving it multiple target ids and name.
/// \notes This is to refer to an overloaded function.
basic_cpp_entity_ref(std::vector<cpp_entity_id> target_ids, std::string target_name)
: target_(std::move(target_ids)), name_(std::move(target_name))
{
}
/// \returns The name of the reference, as spelled in the source code.
const std::string& name() const noexcept
{
return name_;
}
/// \returns The [cppast::cpp_entity_id]() of the entity it refers to.
const cpp_entity_id& id() const noexcept
/// \returns Whether or not it refers to multiple entities.
bool is_overloaded() const noexcept
{
return target_;
return target_.has_value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
}
/// \returns An optional reference to the [cppast::cpp_entity]() it refers to.
type_safe::optional_ref<const T> get(const cpp_entity_index& idx) const noexcept
/// \returns The number of entities it refers to.
type_safe::size_t no_overloaded() const noexcept
{
auto entity = idx.lookup(target_);
if (!entity)
return type_safe::nullopt;
DEBUG_ASSERT(Predicate{}(entity.value()), detail::precondition_error_handler{},
"predicate not fulfilled");
return static_cast<const T&>(entity.value());
return id().size();
}
/// \returns An array reference to the id or ids it refers to.
type_safe::array_ref<const cpp_entity_id> id() const noexcept
{
if (is_overloaded())
{
auto& vec = target_.value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
return type_safe::ref(vec.data(), vec.size());
}
else
{
auto& id = target_.value(type_safe::variant_type<cpp_entity_id>{});
return type_safe::ref(&id, 1u);
}
}
private:
cpp_entity_id target_;
std::string name_;
class ref_impl
{
public:
ref_impl(type_safe::object_ref<const cpp_entity_index> idx,
type_safe::array_ref<const cpp_entity_id> ids)
: ids_(ids), index_(idx)
{
}
class iterator
{
public:
using value_type = type_safe::optional_ref<const T>;
using reference = value_type;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
reference operator*() const noexcept
{
return (*ref_)[i_];
}
pointer operator->() const noexcept
{
return &(*ref_)[i_];
}
iterator& operator++() noexcept
{
++i_;
return *this;
}
iterator operator++(int)noexcept
{
auto tmp = *this;
++(*this);
return tmp;
}
friend bool operator==(const iterator& a, const iterator& b) noexcept
{
return a.ref_ == b.ref_ && a.i_ == b.i_;
}
friend bool operator!=(const iterator& a, const iterator& b) noexcept
{
return !(a == b);
}
private:
iterator(type_safe::object_ref<const ref_impl> ref, type_safe::index_t i)
: ref_(ref), i_(i)
{
}
type_safe::object_ref<const ref_impl> ref_;
type_safe::index_t i_;
friend ref_impl;
};
iterator begin() const noexcept
{
return iterator(type_safe::ref(*this), 0u);
}
iterator end() const noexcept
{
return iterator(type_safe::ref(*this), size());
}
type_safe::optional_ref<const T> operator[](type_safe::index_t i) const
{
return index_->lookup(ids_[i]).map([](const cpp_entity& e) -> const T& {
DEBUG_ASSERT(Predicate{}(e), detail::precondition_error_handler{},
"predicate not fulfilled");
return static_cast<const T&>(e);
});
}
type_safe::size_t size() const noexcept
{
return ids_.size();
}
private:
type_safe::array_ref<const cpp_entity_id> ids_;
type_safe::object_ref<const cpp_entity_index> index_;
};
public:
/// \returns An array reference to the entities it refers to.
/// The return type provides `operator[]` + `size()`,
/// as well as `begin()` and `end()` returning forward iterators.
/// \exclude return
ref_impl get(const cpp_entity_index& idx) const noexcept
{
return ref_impl(type_safe::ref(idx), id());
}
private:
type_safe::variant<cpp_entity_id, std::vector<cpp_entity_id>> target_;
std::string name_;
};
/// \exclude

View file

@ -101,8 +101,9 @@ namespace cppast
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind)
: cpp_entity(target.name()), target_(target.id()), kind_(kind)
: cpp_entity(target.name()), target_(target.id()[0u]), kind_(kind)
{
DEBUG_ASSERT(!target.is_overloaded(), detail::precondition_error_handler{});
}
cpp_entity_id target_;

View file

@ -183,10 +183,10 @@ namespace cppast
/// \effects Sets the entity that is being templated and the primary template.
cpp_template_specialization(std::unique_ptr<cpp_entity> entity,
const cpp_template_ref& templ)
: cpp_template(std::move(entity)), templ_(templ.id())
: cpp_template(std::move(entity)), templ_(templ.id()[0u])
{
DEBUG_ASSERT(templ.name() == entity->name(), detail::precondition_error_handler{},
"invalid name of template ref");
DEBUG_ASSERT(!templ.is_overloaded() && templ.name() == entity->name(),
detail::precondition_error_handler{}, "invalid name of template ref");
}
private:

View file

@ -123,9 +123,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
namespace
{
cpp_entity_id parse_entity_target_cursor(const CXCursor& cur)
std::vector<cpp_entity_id> parse_entity_target_cursor(const CXCursor& cur)
{
cpp_entity_id result("");
std::vector<cpp_entity_id> result;
detail::visit_children(cur,
[&](const CXCursor& child) {
switch (clang_getCursorKind(child))
@ -136,7 +136,7 @@ namespace
case CXCursor_VariableRef:
{
auto referenced = clang_getCursorReferenced(child);
result = detail::get_entity_id(referenced);
result.push_back(detail::get_entity_id(referenced));
break;
}
@ -144,12 +144,9 @@ namespace
{
auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::assert_handler{});
if (size == 1u)
result = detail::get_entity_id(
clang_getOverloadedDecl(child, 0u));
else
result = detail::get_entity_id(
clang_getCursorReferenced(child));
for (auto i = 0u; i != size; ++i)
result.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, i)));
break;
}

View file

@ -179,7 +179,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_private);
REQUIRE(!base.is_virtual());
auto entity = base.entity().get(idx);
REQUIRE(!base.entity().is_overloaded());
auto entity = base.entity().get(idx)[0u];
REQUIRE(entity);
REQUIRE(entity.value().name() == "a");
}
@ -188,7 +189,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_private);
REQUIRE(!base.is_virtual());
auto entity = base.entity().get(idx);
REQUIRE(!base.entity().is_overloaded());
auto entity = base.entity().get(idx)[0u];
REQUIRE(entity);
REQUIRE(entity.value().name() == "d");
}
@ -214,7 +216,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_public);
REQUIRE(!base.is_virtual());
auto entity = base.entity().get(idx);
REQUIRE(!base.entity().is_overloaded());
auto entity = base.entity().get(idx)[0u];
REQUIRE(entity);
REQUIRE(entity.value().name() == "base");
REQUIRE(full_name(entity.value()) == "ns::base");
@ -224,7 +227,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_protected);
REQUIRE(base.is_virtual());
auto entity = base.entity().get(idx);
REQUIRE(!base.entity().is_overloaded());
auto entity = base.entity().get(idx)[0u];
REQUIRE(entity);
REQUIRE(entity.value().name() == "e");
}
@ -250,7 +254,8 @@ struct g
REQUIRE(base.access_specifier() == cpp_public);
REQUIRE(!base.is_virtual());
auto entity = base.entity().get(idx);
REQUIRE(!base.entity().is_overloaded());
auto entity = base.entity().get(idx)[0u];
REQUIRE(entity);
REQUIRE(entity.value().name() == "base");
REQUIRE(full_name(entity.value()) == "ns::base");

View file

@ -77,8 +77,9 @@ namespace f = outer::c;
const char* target_full_name) {
auto& target = alias.target();
REQUIRE(target.name() == target_name);
REQUIRE(!target.is_overloaded());
auto entity = target.get(idx);
auto entity = target.get(idx)[0u];
REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name);
};
@ -132,8 +133,9 @@ 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();
REQUIRE(!target.is_overloaded());
auto entity = target.get(idx);
auto entity = target.get(idx)[0u];
REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name);
};
@ -164,7 +166,6 @@ using namespace outer::ns;
TEST_CASE("cpp_using_declaration")
{
// TODO: test overloaded functions
auto code = R"(
namespace ns1
{
@ -193,34 +194,49 @@ namespace outer
using outer::ns::c;
using outer::c;
namespace ns
{
void d(int);
void d(float);
}
using ns::d;
)";
cpp_entity_index idx;
auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name) {
auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name,
unsigned no) {
auto target = decl.target();
auto entity = target.get(idx);
REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name);
REQUIRE((target.no_overloaded() == no));
for (auto entity : target.get(idx))
{
REQUIRE(entity);
REQUIRE(full_name(entity.value()) == target_full_name);
}
};
auto file = parse(idx, "cpp_using_declaration.cpp", code);
auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) {
REQUIRE(decl.name().empty());
if (decl.target().name() == "ns1::a")
check_declaration(decl, "ns1::a");
check_declaration(decl, "ns1::a", 1u);
else if (decl.target().name() == "ns2::b")
check_declaration(decl, "ns2::b");
check_declaration(decl, "ns2::b", 1u);
else if (decl.target().name() == "ns::c")
{
check_parent(decl, "outer", "");
check_declaration(decl, "outer::ns::c");
check_declaration(decl, "outer::ns::c", 1u);
}
else if (decl.target().name() == "outer::ns::c")
check_declaration(decl, "outer::ns::c");
check_declaration(decl, "outer::ns::c", 1u);
else if (decl.target().name() == "outer::c")
check_declaration(decl, "outer::ns::c");
check_declaration(decl, "outer::ns::c", 1u);
else if (decl.target().name() == "ns::d")
check_declaration(decl, "ns::d", 2u);
else
REQUIRE(false);
});
REQUIRE(count == 5u);
REQUIRE(count == 6u);
}

View file

@ -28,7 +28,9 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
auto user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized).entity();
if (user_parsed.name() != user_synthesized.name())
return false;
auto entity = user_parsed.get(idx);
else if (user_parsed.is_overloaded())
return false;
auto entity = user_parsed.get(idx)[0u];
return entity.has_value() && (entity.value().name().empty()
|| full_name(entity.value()) == user_parsed.name());
}