Add support for overloaded entity references
This commit is contained in:
parent
cb9ad57e87
commit
08717b2ae7
8 changed files with 197 additions and 47 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue