diff --git a/include/cppast/cpp_entity.hpp b/include/cppast/cpp_entity.hpp index 263476c..cf760e0 100644 --- a/include/cppast/cpp_entity.hpp +++ b/include/cppast/cpp_entity.hpp @@ -107,7 +107,7 @@ namespace cppast void on_insert(const cpp_entity& parent) noexcept { - parent_ = parent; + parent_ = type_safe::ref(parent); } std::string name_; diff --git a/include/cppast/cpp_entity_index.hpp b/include/cppast/cpp_entity_index.hpp index 919bb0a..8155565 100644 --- a/include/cppast/cpp_entity_index.hpp +++ b/include/cppast/cpp_entity_index.hpp @@ -5,19 +5,19 @@ #ifndef CPPAST_CPP_ENTITY_INDEX_HPP_INCLUDED #define CPPAST_CPP_ENTITY_INDEX_HPP_INCLUDED -#include #include #include +#include +#include #include #include #include -#include - namespace cppast { class cpp_entity; + class cpp_namespace; /// \exclude namespace detail @@ -36,7 +36,7 @@ namespace cppast /// /// It is comparable for equality. struct cpp_entity_id : type_safe::strong_typedef, - type_safe::strong_typedef_op::equality_comparison + type_safe::strong_typedef_op::equality_comparison { explicit cpp_entity_id(const std::string& str) : cpp_entity_id(str.c_str()) { @@ -66,58 +66,42 @@ namespace cppast /// It will override any previously registered declarations of the same entity. /// \requires If the entity has been registered before, it must be as declaration, /// and the entity must live as long as the index lives. + /// \requires The entity must not be a namespace. /// \notes This operation is thread safe. void register_definition(cpp_entity_id id, - type_safe::object_ref entity) const - { - std::lock_guard lock(mutex_); - auto result = map_.emplace(std::move(id), value(entity, true)); - if (!result.second) - { - // already in map, override declaration - auto& value = result.first->second; - DEBUG_ASSERT(!value.is_definition, detail::precondition_error_handler{}, - "duplicate entity registration"); - value.is_definition = true; - value.entity = entity; - } - } + type_safe::object_ref entity) const; /// \effects Registers a new [cppast::cpp_entity]() which is a declaration. /// Only the first declaration will be registered. /// \requires The entity must live as long as the index lives. - /// \notes This operaiton is thread safe. - void register_forward_declaration(cpp_entity_id id, - type_safe::object_ref entity) const - { - std::lock_guard lock(mutex_); - map_.emplace(std::move(id), value(entity, false)); - } - - /// \returns A [ts::optional_ref]() corresponding to the entity of the given [cppast::cpp_entity_id](). - /// If no definition has been registered, it return the first declaration that was registered. + /// \requires The entity must not be a namespace. /// \notes This operation is thread safe. - type_safe::optional_ref lookup(const cpp_entity_id& id) const noexcept - { - std::lock_guard lock(mutex_); - auto iter = map_.find(id); - if (iter == map_.end()) - return {}; - return iter->second.entity.get(); - } + void register_forward_declaration(cpp_entity_id id, + type_safe::object_ref entity) const; + + /// \effects Registers a new [cppast::cpp_namespace](). + /// \notes The namespace object must live as long as the index lives. + /// \notes This operation is thread safe. + void register_namespace(cpp_entity_id id, + type_safe::object_ref ns) const; + + /// \returns A [ts::optional_ref]() corresponding to the entity(/ies) of the given [cppast::cpp_entity_id](). + /// If no definition has been registered, it return the first declaration that was registered. + /// If the id resolves to a namespaces, returns an empty optional. + /// \notes This operation is thread safe. + type_safe::optional_ref lookup(const cpp_entity_id& id) const noexcept; /// \returns A [ts::optional_ref]() corresponding to the entity of the given [cppast::cpp_entity_id](). /// If no definition has been registered, it returns an empty optional. /// \notes This operation is thread safe. type_safe::optional_ref lookup_definition(const cpp_entity_id& id) const - noexcept - { - std::lock_guard lock(mutex_); - auto iter = map_.find(id); - if (iter == map_.end() || !iter->second.is_definition) - return {}; - return iter->second.entity.get(); - } + noexcept; + + /// \returns A [ts::array_ref]() of references to all namespaces matching the given [cppast::cpp_entity_id](). + /// If no namespace is found, it returns an empty array reference. + /// \notes This operation is thread safe. + auto lookup_namespace(const cpp_entity_id& id) const noexcept + -> type_safe::array_ref>; private: struct hash @@ -141,6 +125,9 @@ namespace cppast mutable std::mutex mutex_; mutable std::unordered_map map_; + mutable std::unordered_map>, hash> + ns_; }; } // namespace cppast diff --git a/include/cppast/cpp_entity_ref.hpp b/include/cppast/cpp_entity_ref.hpp index c1c89e6..29afd60 100644 --- a/include/cppast/cpp_entity_ref.hpp +++ b/include/cppast/cpp_entity_ref.hpp @@ -70,110 +70,43 @@ namespace cppast } } - private: - class ref_impl - { - public: - ref_impl(type_safe::object_ref idx, - type_safe::array_ref ids) - : ids_(ids), index_(idx) - { - } - - class iterator - { - public: - using value_type = type_safe::optional_ref; - 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 ref, type_safe::index_t i) - : ref_(ref), i_(i) - { - } - - type_safe::object_ref 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 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(e); - }); - } - - type_safe::size_t size() const noexcept - { - return ids_.size(); - } - - private: - type_safe::array_ref ids_; - type_safe::object_ref 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 + std::vector> get(const cpp_entity_index& idx) const { - return ref_impl(type_safe::ref(idx), id()); + std::vector> result; + get_impl(std::is_convertible{}, result, idx); + return result; } private: + void get_impl(std::true_type, std::vector>& result, + const cpp_entity_index& idx) const + { + for (auto& cur : id()) + for (auto& ns : idx.lookup_namespace(cur)) + result.push_back(ns); + if (!std::is_same::value) + get_impl(std::false_type{}, result, idx); + } + + void get_impl(std::false_type, std::vector>& result, + const cpp_entity_index& idx) const + { + for (auto& cur : id()) + { + auto entity = idx.lookup(cur).map([](const cpp_entity& e) { + DEBUG_ASSERT(Predicate{}(e), detail::precondition_error_handler{}, + "invalid entity type"); + return type_safe::ref(static_cast(e)); + }); + if (entity) + result.push_back(type_safe::ref(entity.value())); + } + } + type_safe::variant> target_; std::string name_; }; diff --git a/include/cppast/cpp_forward_declarable.hpp b/include/cppast/cpp_forward_declarable.hpp index a81eaf6..d7e51db 100644 --- a/include/cppast/cpp_forward_declarable.hpp +++ b/include/cppast/cpp_forward_declarable.hpp @@ -77,9 +77,9 @@ namespace cppast return idx .lookup_definition(entity.definition().value()) // downcast - .map([](const cpp_entity& e) -> const T& { + .map([](const cpp_entity& e) { DEBUG_ASSERT(e.kind() == T::kind(), detail::assert_handler{}); - return static_cast(e); + return type_safe::ref(static_cast(e)); }); } diff --git a/include/cppast/cpp_namespace.hpp b/include/cppast/cpp_namespace.hpp index 3c2ac63..166d1ae 100644 --- a/include/cppast/cpp_namespace.hpp +++ b/include/cppast/cpp_namespace.hpp @@ -46,7 +46,7 @@ namespace cppast /// \returns The finished namespace. std::unique_ptr finish(const cpp_entity_index& idx, cpp_entity_id id) { - idx.register_definition(std::move(id), type_safe::ref(*namespace_)); + idx.register_namespace(std::move(id), type_safe::ref(*namespace_)); return std::move(namespace_); } diff --git a/include/cppast/cpp_variable_base.hpp b/include/cppast/cpp_variable_base.hpp index 394c374..fcd3a7d 100644 --- a/include/cppast/cpp_variable_base.hpp +++ b/include/cppast/cpp_variable_base.hpp @@ -26,7 +26,7 @@ namespace cppast /// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the default value. type_safe::optional_ref default_value() const noexcept { - return *default_; + return type_safe::ref(*default_); } protected: diff --git a/include/cppast/detail/intrusive_list.hpp b/include/cppast/detail/intrusive_list.hpp index ee68e89..cbc4c1b 100644 --- a/include/cppast/detail/intrusive_list.hpp +++ b/include/cppast/detail/intrusive_list.hpp @@ -130,7 +130,7 @@ namespace cppast if (last_) { auto ptr = intrusive_list_access::set_next(last_.value(), std::move(obj)); - last_ = *ptr; + last_ = type_safe::ref(*ptr); } else { diff --git a/src/cpp_entity_index.cpp b/src/cpp_entity_index.cpp new file mode 100644 index 0000000..f693685 --- /dev/null +++ b/src/cpp_entity_index.cpp @@ -0,0 +1,74 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include +#include +#include + +using namespace cppast; + +void cpp_entity_index::register_definition(cpp_entity_id id, + type_safe::object_ref entity) const +{ + DEBUG_ASSERT(entity->kind() != cpp_entity_kind::namespace_t, + detail::precondition_error_handler{}, "must not be a namespace"); + std::lock_guard lock(mutex_); + auto result = map_.emplace(std::move(id), value(entity, true)); + if (!result.second) + { + // already in map, override declaration + auto& value = result.first->second; + DEBUG_ASSERT(!value.is_definition, detail::precondition_error_handler{}, + "duplicate entity registration"); + value.is_definition = true; + value.entity = entity; + } +} + +void cpp_entity_index::register_forward_declaration( + cpp_entity_id id, type_safe::object_ref entity) const +{ + std::lock_guard lock(mutex_); + map_.emplace(std::move(id), value(entity, false)); +} + +void cpp_entity_index::register_namespace(cpp_entity_id id, + type_safe::object_ref ns) const +{ + std::lock_guard lock(mutex_); + ns_[std::move(id)].push_back(ns); +} + +type_safe::optional_ref cpp_entity_index::lookup(const cpp_entity_id& id) const + noexcept +{ + std::lock_guard lock(mutex_); + auto iter = map_.find(id); + if (iter == map_.end()) + return {}; + return type_safe::ref(iter->second.entity.get()); +} + +type_safe::optional_ref cpp_entity_index::lookup_definition( + const cpp_entity_id& id) const noexcept +{ + std::lock_guard lock(mutex_); + auto iter = map_.find(id); + if (iter == map_.end() || !iter->second.is_definition) + return {}; + return type_safe::ref(iter->second.entity.get()); +} + +auto cpp_entity_index::lookup_namespace(const cpp_entity_id& id) const noexcept + -> type_safe::array_ref> +{ + std::lock_guard lock(mutex_); + auto iter = ns_.find(id); + if (iter == ns_.end()) + return nullptr; + auto& vec = iter->second; + return type_safe::ref(vec.data(), vec.size()); +} diff --git a/test/cpp_class.cpp b/test/cpp_class.cpp index 31aef08..16f5533 100644 --- a/test/cpp_class.cpp +++ b/test/cpp_class.cpp @@ -180,9 +180,9 @@ struct g REQUIRE(!base.is_virtual()); REQUIRE(!base.entity().is_overloaded()); - auto entity = base.entity().get(idx)[0u]; - REQUIRE(entity); - REQUIRE(entity.value().name() == "a"); + auto entities = base.entity().get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(entities[0u]->name() == "a"); } else if (base.name() == "d") { @@ -190,9 +190,9 @@ struct g REQUIRE(!base.is_virtual()); REQUIRE(!base.entity().is_overloaded()); - auto entity = base.entity().get(idx)[0u]; - REQUIRE(entity); - REQUIRE(entity.value().name() == "d"); + auto entities = base.entity().get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(entities[0u]->name() == "d"); } else REQUIRE(false); @@ -217,10 +217,10 @@ struct g REQUIRE(!base.is_virtual()); 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"); + auto entities = base.entity().get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(entities[0u]->name() == "base"); + REQUIRE(full_name(*entities[0u]) == "ns::base"); } else if (base.name() == "e") { @@ -228,9 +228,9 @@ struct g REQUIRE(base.is_virtual()); REQUIRE(!base.entity().is_overloaded()); - auto entity = base.entity().get(idx)[0u]; - REQUIRE(entity); - REQUIRE(entity.value().name() == "e"); + auto entities = base.entity().get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(entities[0u]->name() == "e"); } else REQUIRE(false); @@ -255,10 +255,10 @@ struct g REQUIRE(!base.is_virtual()); 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"); + auto entities = base.entity().get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(entities[0u]->name() == "base"); + REQUIRE(full_name(*entities[0u]) == "ns::base"); } else REQUIRE(false); diff --git a/test/cpp_namespace.cpp b/test/cpp_namespace.cpp index f9df545..a93afd6 100644 --- a/test/cpp_namespace.cpp +++ b/test/cpp_namespace.cpp @@ -54,18 +54,18 @@ namespace c TEST_CASE("cpp_namespace_alias") { auto code = R"( -namespace ns1 {} -namespace ns2 {} +namespace outer {} +namespace ns {} -namespace a = ns1; -namespace b = ns2; +namespace a = outer; +namespace b = ns; namespace outer { namespace ns {} namespace c = ns; - namespace d = ns1; + namespace d = ::outer; } namespace e = outer::ns; @@ -74,36 +74,37 @@ namespace f = outer::c; cpp_entity_index idx; auto check_alias = [&](const cpp_namespace_alias& alias, const char* target_name, - const char* target_full_name) { + const char* target_full_name, unsigned no) { auto& target = alias.target(); REQUIRE(target.name() == target_name); REQUIRE(!target.is_overloaded()); - auto entity = target.get(idx)[0u]; - REQUIRE(entity); - REQUIRE(full_name(entity.value()) == target_full_name); + auto entities = target.get(idx); + REQUIRE(entities.size() == no); + for (auto& entity : entities) + REQUIRE(full_name(*entity) == target_full_name); }; auto file = parse(idx, "cpp_namespace_alias.cpp", code); auto count = test_visit(*file, [&](const cpp_namespace_alias& alias) { if (alias.name() == "a") - check_alias(alias, "ns1", "ns1"); + check_alias(alias, "outer", "outer", 2u); else if (alias.name() == "b") - check_alias(alias, "ns2", "ns2"); + check_alias(alias, "ns", "ns", 1u); else if (alias.name() == "c") { check_parent(alias, "outer", "outer::c"); - check_alias(alias, "ns", "outer::ns"); + check_alias(alias, "ns", "outer::ns", 1u); } else if (alias.name() == "d") { check_parent(alias, "outer", "outer::d"); - check_alias(alias, "ns1", "ns1"); + check_alias(alias, "::outer", "outer", 2u); } else if (alias.name() == "e") - check_alias(alias, "outer::ns", "outer::ns"); + check_alias(alias, "outer::ns", "outer::ns", 1u); else if (alias.name() == "f") - check_alias(alias, "outer::c", "outer::ns"); + check_alias(alias, "outer::c", "outer::ns", 1u); else REQUIRE(false); }); @@ -135,9 +136,9 @@ using namespace outer::ns; auto target = directive.target(); REQUIRE(!target.is_overloaded()); - auto entity = target.get(idx)[0u]; - REQUIRE(entity); - REQUIRE(full_name(entity.value()) == target_full_name); + auto entities = target.get(idx); + REQUIRE(entities.size() == 1u); + REQUIRE(full_name(*entities[0u]) == target_full_name); }; auto file = parse(idx, "cpp_using_directive.cpp", code); @@ -195,13 +196,13 @@ namespace outer using outer::ns::c; using outer::c; -namespace ns +namespace ns1 { void d(int); void d(float); } -using ns::d; +using ns1::d; )"; cpp_entity_index idx; @@ -211,8 +212,7 @@ using ns::d; REQUIRE((target.no_overloaded() == no)); for (auto entity : target.get(idx)) { - REQUIRE(entity); - REQUIRE(full_name(entity.value()) == target_full_name); + REQUIRE(full_name(*entity) == target_full_name); } }; @@ -233,8 +233,8 @@ using ns::d; check_declaration(decl, "outer::ns::c", 1u); else if (decl.target().name() == "outer::c") check_declaration(decl, "outer::ns::c", 1u); - else if (decl.target().name() == "ns::d") - check_declaration(decl, "ns::d", 2u); + else if (decl.target().name() == "ns1::d") + check_declaration(decl, "ns1::d", 2u); else REQUIRE(false); }); diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index ad51c9e..8950084 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -30,9 +30,9 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ return false; 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()); + auto entities = user_parsed.get(idx); + REQUIRE(entities.size() == 1u); + return entities[0u]->name().empty() || full_name(*entities[0u]) == user_parsed.name(); } case cpp_type_kind::cv_qualified: diff --git a/test/cpp_variable.cpp b/test/cpp_variable.cpp index f114439..d6add83 100644 --- a/test/cpp_variable.cpp +++ b/test/cpp_variable.cpp @@ -69,12 +69,15 @@ static struct {} l; else if (var.name() == "b") check_variable(var, *cpp_builtin_type::build("unsigned long long"), // unexposed due to implicit cast, I think - *cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "42"), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_builtin_type::build("int"), + "42")), cpp_storage_class_none, false, false); else if (var.name() == "c") check_variable(var, *cpp_builtin_type::build("float"), - *cpp_unexposed_expression::build(cpp_builtin_type::build("float"), - "3.f+0.14f"), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_builtin_type::build("float"), + "3.f+0.14f")), cpp_storage_class_none, false, false); else if (var.name() == "d") check_variable(var, *int_type, nullptr, cpp_storage_class_extern, false, true); @@ -88,7 +91,9 @@ static struct {} l; else if (var.name() == "h") check_variable(var, *cpp_cv_qualified_type::build(cpp_builtin_type::build("int"), cpp_cv_const), - *cpp_literal_expression::build(cpp_builtin_type::build("int"), "12"), + type_safe::ref( + *cpp_literal_expression::build(cpp_builtin_type::build("int"), + "12")), cpp_storage_class_none, true, false); else if (var.name() == "i") check_variable(var, @@ -99,18 +104,20 @@ static struct {} l; cpp_type_ref(cpp_entity_id(""), "bar")), cpp_cv_const), - *cpp_unexposed_expression::build(cpp_user_defined_type::build( - cpp_type_ref(cpp_entity_id(""), - "bar")), - "bar()"), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), + "bar")), + "bar()")), cpp_storage_class_none, false, false); else if (var.name() == "k") check_variable(var, *cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "baz")), - *cpp_unexposed_expression::build(cpp_user_defined_type::build( - cpp_type_ref(cpp_entity_id(""), - "baz")), - "{}"), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), + "baz")), + "{}")), cpp_storage_class_none, false, false); else if (var.name() == "l") check_variable(var, *cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "")), diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 4158a59..286a6a9 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -33,7 +33,10 @@ inline std::unique_ptr parse(const cppast::cpp_entity_index& i static stderr_diagnostic_logger logger; libclang_parser p(type_safe::ref(logger)); - return p.parse(idx, name, config); + + auto result = p.parse(idx, name, config); + REQUIRE(!logger.error_logged()); + return result; } template