diff --git a/include/cppast/cpp_class.hpp b/include/cppast/cpp_class.hpp index 4a5cfca..e2465cb 100644 --- a/include/cppast/cpp_class.hpp +++ b/include/cppast/cpp_class.hpp @@ -74,17 +74,19 @@ namespace cppast /// \returns A newly created base class specifier. /// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), /// as nothing can refer to the specifier itself. - static std::unique_ptr build(const cpp_type_ref& base, + static std::unique_ptr build(std::string name, + std::unique_ptr base, cpp_access_specifier_kind access, bool is_virtual) { - return std::unique_ptr(new cpp_base_class(base, access, is_virtual)); + return std::unique_ptr( + new cpp_base_class(std::move(name), std::move(base), access, is_virtual)); } - /// \returns An entity reference to the base class. - cpp_type_ref entity() const + /// \returns The type of the base class. + const cpp_type& type() const { - return cpp_type_ref(base_id_, name()); + return *type_; } /// \returns The access specifier of the base class. @@ -100,15 +102,15 @@ 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()[0u]), access_(access), virtual_(is_virtual) + cpp_base_class(std::string name, std::unique_ptr base, + cpp_access_specifier_kind access, bool is_virtual) + : cpp_entity(std::move(name)), type_(std::move(base)), 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_id base_id_; + std::unique_ptr type_; cpp_access_specifier_kind access_; bool virtual_; }; @@ -142,10 +144,11 @@ namespace cppast } /// \effects Builds a [cppast::cpp_base_class]() and adds it. - void base_class(const cpp_type_ref& base, cpp_access_specifier_kind access, - bool is_virtual) + void base_class(std::string name, std::unique_ptr type, + cpp_access_specifier_kind access, bool is_virtual) { - add_base_class(cpp_base_class::build(base, access, is_virtual)); + add_base_class( + cpp_base_class::build(std::move(name), std::move(type), access, is_virtual)); } /// \effects Adds a new base class. @@ -179,10 +182,17 @@ namespace cppast /// \effects Marks the class as forward declaration. /// \returns The finished class. - /// \notes It will not be registered, as it is not the main definition. std::unique_ptr finish_declaration(const cpp_entity_index& idx, cpp_entity_id definition_id); + /// \effects Returns the finished class without registering it. + /// \notes This is intended for templated classes only. + std::unique_ptr finish(); + + /// \effects Returns the finish class without registering it and marks it as forward declaration. + /// \notes This is intended for templated classes only. + std::unique_ptr finish_declaration(cpp_entity_id definition_id); + private: std::unique_ptr class_; }; diff --git a/include/cppast/cpp_class_template.hpp b/include/cppast/cpp_class_template.hpp index 30677a1..5bae936 100644 --- a/include/cppast/cpp_class_template.hpp +++ b/include/cppast/cpp_class_template.hpp @@ -14,6 +14,8 @@ namespace cppast class cpp_class_template final : public cpp_template { public: + static cpp_entity_kind kind() noexcept; + /// Builder for [cppast::cpp_class_template](). class builder : public basic_builder { diff --git a/include/cppast/cpp_template.hpp b/include/cppast/cpp_template.hpp index 64aba15..e481045 100644 --- a/include/cppast/cpp_template.hpp +++ b/include/cppast/cpp_template.hpp @@ -58,9 +58,14 @@ namespace cppast /// \effects Registers the template. /// \returns The finished template. - std::unique_ptr finish(const cpp_entity_index& idx, cpp_entity_id id) + std::unique_ptr finish(const cpp_entity_index& idx, cpp_entity_id id, + bool is_definition) { - idx.register_definition(std::move(id), type_safe::cref(*template_entity)); + if (is_definition) + idx.register_definition(std::move(id), type_safe::cref(*template_entity)); + else + idx.register_forward_declaration(std::move(id), + type_safe::cref(*template_entity)); return std::move(template_entity); } diff --git a/src/cpp_class.cpp b/src/cpp_class.cpp index 2f84aa6..3878d1d 100644 --- a/src/cpp_class.cpp +++ b/src/cpp_class.cpp @@ -23,6 +23,17 @@ std::unique_ptr cpp_class::builder::finish_declaration(const cpp_enti return std::move(class_); } +std::unique_ptr cpp_class::builder::finish() +{ + return std::move(class_); +} + +std::unique_ptr cpp_class::builder::finish_declaration(cpp_entity_id definition_id) +{ + class_->set_definition(definition_id); + return std::move(class_); +} + const char* cppast::to_string(cpp_class_kind kind) noexcept { switch (kind) diff --git a/src/cpp_class_template.cpp b/src/cpp_class_template.cpp index d645577..263e8ac 100644 --- a/src/cpp_class_template.cpp +++ b/src/cpp_class_template.cpp @@ -8,11 +8,16 @@ using namespace cppast; -cpp_entity_kind cpp_class_template::do_get_entity_kind() const noexcept +cpp_entity_kind cpp_class_template::kind() noexcept { return cpp_entity_kind::class_template_t; } +cpp_entity_kind cpp_class_template::do_get_entity_kind() const noexcept +{ + return kind(); +} + cpp_entity_kind cpp_class_template_specialization::do_get_entity_kind() const noexcept { return cpp_entity_kind::class_template_specialization_t; diff --git a/src/libclang/class_parser.cpp b/src/libclang/class_parser.cpp index 0d236e6..7acf38a 100644 --- a/src/libclang/class_parser.cpp +++ b/src/libclang/class_parser.cpp @@ -14,7 +14,10 @@ namespace { cpp_class_kind parse_class_kind(const CXCursor& cur) { - switch (clang_getCursorKind(cur)) + auto kind = clang_getTemplateCursorKind(cur); + if (kind == CXCursor_NoDeclFound) + kind = clang_getCursorKind(cur); + switch (kind) { case CXCursor_ClassDecl: return cpp_class_kind::class_t; @@ -62,7 +65,7 @@ namespace } void add_base_class(cpp_class::builder& builder, const detail::parse_context& context, - const CXCursor& cur) + const CXCursor& cur, const CXCursor& class_cur) { DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{}); auto access = convert_access(cur); @@ -82,9 +85,8 @@ namespace while (!stream.done()) name += stream.get().c_str(); - auto type = - cpp_type_ref(detail::get_entity_id(clang_getCursorReferenced(cur)), std::move(name)); - builder.base_class(type, access, is_virtual); + auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur)); + builder.base_class(std::move(name), std::move(type), access, is_virtual); } } @@ -98,14 +100,19 @@ std::unique_ptr detail::parse_cpp_class(const detail::parse_context& if (kind == CXCursor_CXXAccessSpecifier) add_access_specifier(builder, child); else if (kind == CXCursor_CXXBaseSpecifier) - add_base_class(builder, context, child); + add_base_class(builder, context, child, cur); else if (kind == CXCursor_CXXFinalAttr) builder.is_final(); + else if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter + || kind == CXCursor_TemplateTemplateParameter) + return; else if (auto entity = parse_entity(context, child)) builder.add_child(std::move(entity)); }); + auto is_templated = clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound; if (clang_isCursorDefinition(cur)) - return builder.finish(*context.idx, get_entity_id(cur)); + return is_templated ? builder.finish() : builder.finish(*context.idx, get_entity_id(cur)); else - return builder.finish_declaration(*context.idx, get_entity_id(cur)); + return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) : + builder.finish_declaration(*context.idx, get_entity_id(cur)); } diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 744c335..38af015 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -132,6 +132,8 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co return parse_cpp_alias_template(context, cur); case CXCursor_FunctionTemplate: return parse_cpp_function_template(context, cur); + case CXCursor_ClassTemplate: + return parse_cpp_class_template(context, cur); default: break; diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 5301ef7..a48b800 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -133,6 +133,8 @@ namespace cppast const CXCursor& cur); std::unique_ptr parse_cpp_function_template(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_class_template(const parse_context& context, + const CXCursor& cur); // as_template: true, iff currently parsing a template std::unique_ptr parse_entity( diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index fcb0572..3e01020 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -3,6 +3,7 @@ // found in the top-level directory of this distribution. #include +#include #include #include @@ -217,7 +218,7 @@ std::unique_ptr detail::parse_cpp_alias_template(const detail::parse return nullptr; context.comments.match(builder.value().get(), cur); parse_parameters(builder.value(), context, cur); - return builder.value().finish(*context.idx, detail::get_entity_id(cur)); + return builder.value().finish(*context.idx, detail::get_entity_id(cur), true); } std::unique_ptr detail::parse_cpp_function_template( @@ -259,13 +260,14 @@ std::unique_ptr detail::parse_cpp_function_template( std::unique_ptr(static_cast(func.release()))); builder.get().set_comment(std::move(comment)); parse_parameters(builder, context, cur); - return builder.finish(*context.idx, detail::get_entity_id(cur)); + return builder.finish(*context.idx, detail::get_entity_id(cur), + builder.get().function().is_definition()); } namespace { template - void add_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur) + void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur) { detail::tokenizer tokenizer(context.tu, context.file, cur); detail::token_stream stream(tokenizer, cur); @@ -320,10 +322,36 @@ std::unique_ptr detail::try_parse_cpp_function_template_specializati if (!func) return nullptr; + // steal comment + auto comment = type_safe::copy(func->comment()); + func->set_comment(type_safe::nullopt); + cpp_function_template_specialization::builder builder(std::unique_ptr(static_cast(func.release())), cpp_template_ref(detail::get_entity_id(templ), "")); - - add_arguments(builder, context, cur); - return builder.finish(*context.idx, detail::get_entity_id(cur)); + builder.get().set_comment(std::move(comment)); + parse_arguments(builder, context, cur); + return builder.finish(*context.idx, detail::get_entity_id(cur), + builder.get().function().is_definition()); +} + +std::unique_ptr detail::parse_cpp_class_template(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ClassTemplate, detail::assert_handler{}); + + auto c = detail::parse_cpp_class(context, cur); + if (!c) + return nullptr; + + // steal comment + auto comment = type_safe::copy(c->comment()); + c->set_comment(type_safe::nullopt); + + cpp_class_template::builder builder( + std::unique_ptr(static_cast(c.release()))); + builder.get().set_comment(std::move(comment)); + parse_parameters(builder, context, cur); + return builder.finish(*context.idx, detail::get_entity_id(cur), + builder.get().class_().is_definition()); } diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index da4949a..ba34a08 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -451,6 +451,7 @@ namespace case CXType_Vector: case CXType_ObjCInterface: case CXType_ObjCObjectPointer: + case CXType_Dependent: { auto msg = detail::format("unexpected type of kind '", detail::get_type_kind_spelling(type).c_str(), "'"); @@ -543,10 +544,6 @@ namespace case CXType_MemberPointer: return cpp_pointer_type::build(parse_member_pointee_type(context, cur, type)); - // TODO: everything template related - case CXType_Dependent: - break; - // TODO: auto/decltype case CXType_Auto: break; diff --git a/test/cpp_class.cpp b/test/cpp_class.cpp index 16f5533..5b2f6ab 100644 --- a/test/cpp_class.cpp +++ b/test/cpp_class.cpp @@ -179,20 +179,18 @@ struct g REQUIRE(base.access_specifier() == cpp_private); REQUIRE(!base.is_virtual()); - REQUIRE(!base.entity().is_overloaded()); - auto entities = base.entity().get(idx); - REQUIRE(entities.size() == 1u); - REQUIRE(entities[0u]->name() == "a"); + REQUIRE( + equal_types(idx, base.type(), *cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "a")))); } else if (base.name() == "d") { REQUIRE(base.access_specifier() == cpp_private); REQUIRE(!base.is_virtual()); - REQUIRE(!base.entity().is_overloaded()); - auto entities = base.entity().get(idx); - REQUIRE(entities.size() == 1u); - REQUIRE(entities[0u]->name() == "d"); + REQUIRE( + equal_types(idx, base.type(), *cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "d")))); } else REQUIRE(false); @@ -216,21 +214,18 @@ struct g REQUIRE(base.access_specifier() == cpp_public); REQUIRE(!base.is_virtual()); - REQUIRE(!base.entity().is_overloaded()); - auto entities = base.entity().get(idx); - REQUIRE(entities.size() == 1u); - REQUIRE(entities[0u]->name() == "base"); - REQUIRE(full_name(*entities[0u]) == "ns::base"); + REQUIRE(equal_types(idx, base.type(), + *cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "ns::base")))); } else if (base.name() == "e") { REQUIRE(base.access_specifier() == cpp_protected); REQUIRE(base.is_virtual()); - REQUIRE(!base.entity().is_overloaded()); - auto entities = base.entity().get(idx); - REQUIRE(entities.size() == 1u); - REQUIRE(entities[0u]->name() == "e"); + REQUIRE( + equal_types(idx, base.type(), *cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "e")))); } else REQUIRE(false); @@ -254,11 +249,9 @@ struct g REQUIRE(base.access_specifier() == cpp_public); REQUIRE(!base.is_virtual()); - REQUIRE(!base.entity().is_overloaded()); - auto entities = base.entity().get(idx); - REQUIRE(entities.size() == 1u); - REQUIRE(entities[0u]->name() == "base"); - REQUIRE(full_name(*entities[0u]) == "ns::base"); + REQUIRE(equal_types(idx, base.type(), + *cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "ns::base")))); } else REQUIRE(false); diff --git a/test/cpp_class_template.cpp b/test/cpp_class_template.cpp new file mode 100644 index 0000000..3b31647 --- /dev/null +++ b/test/cpp_class_template.cpp @@ -0,0 +1,171 @@ +// 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 + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_class_template") +{ + auto code = R"( +// check everything not related to members first +template +class a {}; + +template +struct b {}; + +template