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:
|
private:
|
||||||
cpp_base_class(const cpp_type_ref& base, cpp_access_specifier_kind access, bool is_virtual)
|
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;
|
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@
|
||||||
#ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
|
#ifndef CPPAST_CPP_ENTITY_REF_HPP_INCLUDED
|
||||||
#define 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>
|
#include <cppast/cpp_entity_index.hpp>
|
||||||
|
|
||||||
namespace cppast
|
namespace cppast
|
||||||
|
|
@ -12,6 +16,10 @@ namespace cppast
|
||||||
enum class cpp_entity_kind;
|
enum class cpp_entity_kind;
|
||||||
|
|
||||||
/// A basic reference to some kind of [cppast::cpp_entity]().
|
/// 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>
|
template <typename T, typename Predicate>
|
||||||
class basic_cpp_entity_ref
|
class basic_cpp_entity_ref
|
||||||
{
|
{
|
||||||
|
|
@ -22,31 +30,151 @@ 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.
|
/// \returns The name of the reference, as spelled in the source code.
|
||||||
const std::string& name() const noexcept
|
const std::string& name() const noexcept
|
||||||
{
|
{
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \returns The [cppast::cpp_entity_id]() of the entity it refers to.
|
/// \returns Whether or not it refers to multiple entities.
|
||||||
const cpp_entity_id& id() const noexcept
|
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.
|
/// \returns The number of entities it refers to.
|
||||||
type_safe::optional_ref<const T> get(const cpp_entity_index& idx) const noexcept
|
type_safe::size_t no_overloaded() const noexcept
|
||||||
{
|
{
|
||||||
auto entity = idx.lookup(target_);
|
return id().size();
|
||||||
if (!entity)
|
}
|
||||||
return type_safe::nullopt;
|
|
||||||
DEBUG_ASSERT(Predicate{}(entity.value()), detail::precondition_error_handler{},
|
/// \returns An array reference to the id or ids it refers to.
|
||||||
"predicate not fulfilled");
|
type_safe::array_ref<const cpp_entity_id> id() const noexcept
|
||||||
return static_cast<const T&>(entity.value());
|
{
|
||||||
|
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:
|
private:
|
||||||
cpp_entity_id target_;
|
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_;
|
std::string name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,9 @@ namespace cppast
|
||||||
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||||
|
|
||||||
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind)
|
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_;
|
cpp_entity_id target_;
|
||||||
|
|
|
||||||
|
|
@ -183,10 +183,10 @@ namespace cppast
|
||||||
/// \effects Sets the entity that is being templated and the primary template.
|
/// \effects Sets the entity that is being templated and the primary template.
|
||||||
cpp_template_specialization(std::unique_ptr<cpp_entity> entity,
|
cpp_template_specialization(std::unique_ptr<cpp_entity> entity,
|
||||||
const cpp_template_ref& templ)
|
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{},
|
DEBUG_ASSERT(!templ.is_overloaded() && templ.name() == entity->name(),
|
||||||
"invalid name of template ref");
|
detail::precondition_error_handler{}, "invalid name of template ref");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -123,9 +123,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
|
||||||
|
|
||||||
namespace
|
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,
|
detail::visit_children(cur,
|
||||||
[&](const CXCursor& child) {
|
[&](const CXCursor& child) {
|
||||||
switch (clang_getCursorKind(child))
|
switch (clang_getCursorKind(child))
|
||||||
|
|
@ -136,7 +136,7 @@ namespace
|
||||||
case CXCursor_VariableRef:
|
case CXCursor_VariableRef:
|
||||||
{
|
{
|
||||||
auto referenced = clang_getCursorReferenced(child);
|
auto referenced = clang_getCursorReferenced(child);
|
||||||
result = detail::get_entity_id(referenced);
|
result.push_back(detail::get_entity_id(referenced));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,12 +144,9 @@ namespace
|
||||||
{
|
{
|
||||||
auto size = clang_getNumOverloadedDecls(child);
|
auto size = clang_getNumOverloadedDecls(child);
|
||||||
DEBUG_ASSERT(size >= 1u, detail::assert_handler{});
|
DEBUG_ASSERT(size >= 1u, detail::assert_handler{});
|
||||||
if (size == 1u)
|
for (auto i = 0u; i != size; ++i)
|
||||||
result = detail::get_entity_id(
|
result.push_back(detail::get_entity_id(
|
||||||
clang_getOverloadedDecl(child, 0u));
|
clang_getOverloadedDecl(child, i)));
|
||||||
else
|
|
||||||
result = detail::get_entity_id(
|
|
||||||
clang_getCursorReferenced(child));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,8 @@ struct g
|
||||||
REQUIRE(base.access_specifier() == cpp_private);
|
REQUIRE(base.access_specifier() == cpp_private);
|
||||||
REQUIRE(!base.is_virtual());
|
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);
|
||||||
REQUIRE(entity.value().name() == "a");
|
REQUIRE(entity.value().name() == "a");
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +189,8 @@ struct g
|
||||||
REQUIRE(base.access_specifier() == cpp_private);
|
REQUIRE(base.access_specifier() == cpp_private);
|
||||||
REQUIRE(!base.is_virtual());
|
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);
|
||||||
REQUIRE(entity.value().name() == "d");
|
REQUIRE(entity.value().name() == "d");
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +216,8 @@ struct g
|
||||||
REQUIRE(base.access_specifier() == cpp_public);
|
REQUIRE(base.access_specifier() == cpp_public);
|
||||||
REQUIRE(!base.is_virtual());
|
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);
|
||||||
REQUIRE(entity.value().name() == "base");
|
REQUIRE(entity.value().name() == "base");
|
||||||
REQUIRE(full_name(entity.value()) == "ns::base");
|
REQUIRE(full_name(entity.value()) == "ns::base");
|
||||||
|
|
@ -224,7 +227,8 @@ struct g
|
||||||
REQUIRE(base.access_specifier() == cpp_protected);
|
REQUIRE(base.access_specifier() == cpp_protected);
|
||||||
REQUIRE(base.is_virtual());
|
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);
|
||||||
REQUIRE(entity.value().name() == "e");
|
REQUIRE(entity.value().name() == "e");
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +254,8 @@ struct g
|
||||||
REQUIRE(base.access_specifier() == cpp_public);
|
REQUIRE(base.access_specifier() == cpp_public);
|
||||||
REQUIRE(!base.is_virtual());
|
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);
|
||||||
REQUIRE(entity.value().name() == "base");
|
REQUIRE(entity.value().name() == "base");
|
||||||
REQUIRE(full_name(entity.value()) == "ns::base");
|
REQUIRE(full_name(entity.value()) == "ns::base");
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,9 @@ namespace f = outer::c;
|
||||||
const char* target_full_name) {
|
const char* target_full_name) {
|
||||||
auto& target = alias.target();
|
auto& target = alias.target();
|
||||||
REQUIRE(target.name() == target_name);
|
REQUIRE(target.name() == target_name);
|
||||||
|
REQUIRE(!target.is_overloaded());
|
||||||
|
|
||||||
auto entity = target.get(idx);
|
auto entity = target.get(idx)[0u];
|
||||||
REQUIRE(entity);
|
REQUIRE(entity);
|
||||||
REQUIRE(full_name(entity.value()) == target_full_name);
|
REQUIRE(full_name(entity.value()) == target_full_name);
|
||||||
};
|
};
|
||||||
|
|
@ -132,8 +133,9 @@ using namespace outer::ns;
|
||||||
cpp_entity_index idx;
|
cpp_entity_index idx;
|
||||||
auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) {
|
auto check_directive = [&](const cpp_using_directive& directive, const char* target_full_name) {
|
||||||
auto target = directive.target();
|
auto target = directive.target();
|
||||||
|
REQUIRE(!target.is_overloaded());
|
||||||
|
|
||||||
auto entity = target.get(idx);
|
auto entity = target.get(idx)[0u];
|
||||||
REQUIRE(entity);
|
REQUIRE(entity);
|
||||||
REQUIRE(full_name(entity.value()) == target_full_name);
|
REQUIRE(full_name(entity.value()) == target_full_name);
|
||||||
};
|
};
|
||||||
|
|
@ -164,7 +166,6 @@ using namespace outer::ns;
|
||||||
|
|
||||||
TEST_CASE("cpp_using_declaration")
|
TEST_CASE("cpp_using_declaration")
|
||||||
{
|
{
|
||||||
// TODO: test overloaded functions
|
|
||||||
auto code = R"(
|
auto code = R"(
|
||||||
namespace ns1
|
namespace ns1
|
||||||
{
|
{
|
||||||
|
|
@ -193,34 +194,49 @@ namespace outer
|
||||||
|
|
||||||
using outer::ns::c;
|
using outer::ns::c;
|
||||||
using outer::c;
|
using outer::c;
|
||||||
|
|
||||||
|
namespace ns
|
||||||
|
{
|
||||||
|
void d(int);
|
||||||
|
void d(float);
|
||||||
|
}
|
||||||
|
|
||||||
|
using ns::d;
|
||||||
)";
|
)";
|
||||||
|
|
||||||
cpp_entity_index idx;
|
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 target = decl.target();
|
||||||
|
REQUIRE((target.no_overloaded() == no));
|
||||||
auto entity = target.get(idx);
|
for (auto entity : target.get(idx))
|
||||||
|
{
|
||||||
REQUIRE(entity);
|
REQUIRE(entity);
|
||||||
REQUIRE(full_name(entity.value()) == target_full_name);
|
REQUIRE(full_name(entity.value()) == target_full_name);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto file = parse(idx, "cpp_using_declaration.cpp", code);
|
auto file = parse(idx, "cpp_using_declaration.cpp", code);
|
||||||
auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) {
|
auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) {
|
||||||
|
REQUIRE(decl.name().empty());
|
||||||
|
|
||||||
if (decl.target().name() == "ns1::a")
|
if (decl.target().name() == "ns1::a")
|
||||||
check_declaration(decl, "ns1::a");
|
check_declaration(decl, "ns1::a", 1u);
|
||||||
else if (decl.target().name() == "ns2::b")
|
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")
|
else if (decl.target().name() == "ns::c")
|
||||||
{
|
{
|
||||||
check_parent(decl, "outer", "");
|
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")
|
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")
|
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
|
else
|
||||||
REQUIRE(false);
|
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();
|
auto user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized).entity();
|
||||||
if (user_parsed.name() != user_synthesized.name())
|
if (user_parsed.name() != user_synthesized.name())
|
||||||
return false;
|
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()
|
return entity.has_value() && (entity.value().name().empty()
|
||||||
|| full_name(entity.value()) == user_parsed.name());
|
|| full_name(entity.value()) == user_parsed.name());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue