From c3d1d978925234a8d5b3189a89e9807a99849b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 6 Apr 2017 11:45:17 +0200 Subject: [PATCH] Add and parse cpp_friend Only works in clang>=4. --- include/cppast/cpp_entity_kind.hpp | 2 + include/cppast/cpp_friend.hpp | 76 ++++++++ include/cppast/cpp_function.hpp | 21 +-- include/cppast/diagnostic.hpp | 2 +- src/CMakeLists.txt | 77 ++++---- src/code_generator.cpp | 38 +++- src/cpp_entity_kind.cpp | 8 + src/cpp_friend.cpp | 26 +++ src/libclang/class_parser.cpp | 51 +++--- src/libclang/friend_parser.cpp | 107 ++++++++++++ src/libclang/function_parser.cpp | 6 +- src/libclang/parse_functions.cpp | 11 +- src/libclang/parse_functions.hpp | 16 +- src/libclang/template_parser.cpp | 4 +- src/visitor.cpp | 1 + test/CMakeLists.txt | 1 + test/cpp_friend.cpp | 270 +++++++++++++++++++++++++++++ test/cpp_function.cpp | 4 - 18 files changed, 629 insertions(+), 92 deletions(-) create mode 100644 include/cppast/cpp_friend.hpp create mode 100644 src/cpp_friend.cpp create mode 100644 src/libclang/friend_parser.cpp create mode 100644 test/cpp_friend.cpp diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index 8480a86..d0ee6e5 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -44,6 +44,8 @@ namespace cppast constructor_t, destructor_t, + friend_t, + template_type_parameter_t, non_type_template_parameter_t, template_template_parameter_t, diff --git a/include/cppast/cpp_friend.hpp b/include/cppast/cpp_friend.hpp new file mode 100644 index 0000000..5e927cf --- /dev/null +++ b/include/cppast/cpp_friend.hpp @@ -0,0 +1,76 @@ +// 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. + +#ifndef CPPAST_CPP_FRIEND_HPP_INCLUDED +#define CPPAST_CPP_FRIEND_HPP_INCLUDED + +#include + +#include +#include +#include + +namespace cppast +{ + /// A [cppast::cpp_entity]() representing a friend declaration. + /// + /// It can either declare or define a `friend` function (template), declare a `friend` class, + /// or refer to an existing type. + class cpp_friend : public cpp_entity, private cpp_entity_container + { + public: + static cpp_entity_kind kind() noexcept; + + /// \returns A newly created friend declaring the given entity as `friend`. + /// \notes The friend declaration itself will not be registered, + /// but the referring entity is. + static std::unique_ptr build(std::unique_ptr e) + { + return std::unique_ptr(new cpp_friend(std::move(e))); + } + + /// \returns A newly created friend declaring the given type as `friend`. + /// \notes It will not be registered. + static std::unique_ptr build(std::unique_ptr type) + { + return std::unique_ptr(new cpp_friend(std::move(type))); + } + + /// \returns An optional reference to the entity it declares as friend, or `nullptr`. + type_safe::optional_ref entity() const noexcept + { + if (begin() == end()) + return nullptr; + return type_safe::ref(*begin()); + } + + /// \returns An optional reference to the type it declares as friend, or `nullptr`. + type_safe::optional_ref type() const noexcept + { + return type_safe::opt_ref(type_.get()); + } + + private: + cpp_friend(std::unique_ptr e) : cpp_entity("") + { + add_child(std::move(e)); + } + + cpp_friend(std::unique_ptr type) : cpp_entity(""), type_(std::move(type)) + { + } + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + std::unique_ptr type_; + + friend cpp_entity_container; + }; + + /// \returns Whether or not the given entity is "friended", + /// that is, its declaration exists as part of a [cppast::cpp_friend]() declaration. + bool is_friended(const cpp_entity& e) noexcept; +} // namespace cppast + +#endif // CPPAST_CPP_FRIEND_HPP_INCLUDED diff --git a/include/cppast/cpp_function.hpp b/include/cppast/cpp_function.hpp index d161fa1..caa889c 100644 --- a/include/cppast/cpp_function.hpp +++ b/include/cppast/cpp_function.hpp @@ -147,8 +147,10 @@ namespace cppast /// \returns The finished function without registering it. /// \notes This is intended for templated functions only. - std::unique_ptr finish(cpp_function_body_kind body_kind) + std::unique_ptr finish(cpp_entity_id id, cpp_function_body_kind body_kind) { + if (!cppast::is_definition(body_kind)) + function->set_definition(id); function->body_ = body_kind; return std::move(function); } @@ -174,8 +176,7 @@ namespace cppast /// A [cppast::cpp_entity]() modelling a C++ function. /// \notes This is not a member function, /// use [cppast::cpp_member_function]() for that. - /// It can be a `static` function of a class, - /// or a `friend`, however. + /// It can be a `static` function of a class, however. class cpp_function final : public cpp_function_base { public: @@ -203,12 +204,6 @@ namespace cppast { function->constexpr_ = true; } - - /// \effects Marks the function as `friend`. - void is_friend() - { - function->friend_ = true; - } }; /// \returns A reference to the return [cppast::cpp_type](). @@ -231,12 +226,6 @@ namespace cppast return constexpr_; } - /// \returns Whether the function is a `friend` function of the parent [cppast::cpp_class](). - bool is_friend() const noexcept - { - return friend_; - } - private: cpp_entity_kind do_get_entity_kind() const noexcept override; @@ -244,14 +233,12 @@ namespace cppast : cpp_function_base(std::move(name)), return_type_(std::move(ret)), storage_(cpp_storage_class_auto), - friend_(false), constexpr_(false) { } std::unique_ptr return_type_; cpp_storage_class_specifiers storage_; - bool friend_; bool constexpr_; }; } // namespace cppast diff --git a/include/cppast/diagnostic.hpp b/include/cppast/diagnostic.hpp index c90e9be..6ac0f72 100644 --- a/include/cppast/diagnostic.hpp +++ b/include/cppast/diagnostic.hpp @@ -56,7 +56,7 @@ namespace cppast if (entity) result += " (" + entity.value() + "):"; } - else if (entity) + else if (entity && !entity.value().empty()) result += entity.value() + ":"; return result; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f466c82..5114b03 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,43 +6,44 @@ set(detail_header ../include/cppast/detail/assert.hpp ../include/cppast/detail/intrusive_list.hpp) set(header - ../include/cppast/code_generator.hpp - ../include/cppast/compile_config.hpp - ../include/cppast/cpp_alias_template.hpp - ../include/cppast/cpp_array_type.hpp - ../include/cppast/cpp_class.hpp - ../include/cppast/cpp_class_template.hpp - ../include/cppast/cpp_decltype_type.hpp - ../include/cppast/cpp_entity.hpp - ../include/cppast/cpp_entity.hpp - ../include/cppast/cpp_entity_container.hpp - ../include/cppast/cpp_entity_index.hpp - ../include/cppast/cpp_entity_kind.hpp - ../include/cppast/cpp_entity_ref.hpp - ../include/cppast/cpp_enum.hpp - ../include/cppast/cpp_expression.hpp - ../include/cppast/cpp_file.hpp - ../include/cppast/cpp_forward_declarable.hpp - ../include/cppast/cpp_function.hpp - ../include/cppast/cpp_function_template.hpp - ../include/cppast/cpp_function_type.hpp - ../include/cppast/cpp_language_linkage.hpp - ../include/cppast/cpp_member_function.hpp - ../include/cppast/cpp_member_variable.hpp - ../include/cppast/cpp_namespace.hpp - ../include/cppast/cpp_preprocessor.hpp - ../include/cppast/cpp_storage_class_specifiers.hpp - ../include/cppast/cpp_template.hpp - ../include/cppast/cpp_template_parameter.hpp - ../include/cppast/cpp_type.hpp - ../include/cppast/cpp_type_alias.hpp - ../include/cppast/cpp_variable.hpp - ../include/cppast/cpp_variable_base.hpp - ../include/cppast/cpp_variable_template.hpp - ../include/cppast/diagnostic.hpp - ../include/cppast/libclang_parser.hpp - ../include/cppast/parser.hpp - ../include/cppast/visitor.hpp) + ../include/cppast/code_generator.hpp + ../include/cppast/compile_config.hpp + ../include/cppast/cpp_alias_template.hpp + ../include/cppast/cpp_array_type.hpp + ../include/cppast/cpp_class.hpp + ../include/cppast/cpp_class_template.hpp + ../include/cppast/cpp_decltype_type.hpp + ../include/cppast/cpp_entity.hpp + ../include/cppast/cpp_entity.hpp + ../include/cppast/cpp_entity_container.hpp + ../include/cppast/cpp_entity_index.hpp + ../include/cppast/cpp_entity_kind.hpp + ../include/cppast/cpp_entity_ref.hpp + ../include/cppast/cpp_enum.hpp + ../include/cppast/cpp_expression.hpp + ../include/cppast/cpp_file.hpp + ../include/cppast/cpp_forward_declarable.hpp + ../include/cppast/cpp_friend.hpp + ../include/cppast/cpp_function.hpp + ../include/cppast/cpp_function_template.hpp + ../include/cppast/cpp_function_type.hpp + ../include/cppast/cpp_language_linkage.hpp + ../include/cppast/cpp_member_function.hpp + ../include/cppast/cpp_member_variable.hpp + ../include/cppast/cpp_namespace.hpp + ../include/cppast/cpp_preprocessor.hpp + ../include/cppast/cpp_storage_class_specifiers.hpp + ../include/cppast/cpp_template.hpp + ../include/cppast/cpp_template_parameter.hpp + ../include/cppast/cpp_type.hpp + ../include/cppast/cpp_type_alias.hpp + ../include/cppast/cpp_variable.hpp + ../include/cppast/cpp_variable_base.hpp + ../include/cppast/cpp_variable_template.hpp + ../include/cppast/diagnostic.hpp + ../include/cppast/libclang_parser.hpp + ../include/cppast/parser.hpp + ../include/cppast/visitor.hpp) set(source code_generator.cpp cpp_alias_template.cpp @@ -54,6 +55,7 @@ set(source cpp_enum.cpp cpp_expression.cpp cpp_file.cpp + cpp_friend.cpp cpp_function.cpp cpp_function_template.cpp cpp_language_linkage.cpp @@ -74,6 +76,7 @@ set(libclang_source libclang/debug_helper.hpp libclang/enum_parser.cpp libclang/expression_parser.cpp + libclang/friend_parser.cpp libclang/function_parser.cpp libclang/language_linkage_parser.cpp libclang/libclang_parser.cpp diff --git a/src/code_generator.cpp b/src/code_generator.cpp index 8df1254..337357a 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -279,6 +280,8 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true); if (output) { + if (is_friended(c)) + output << keyword("friend") << whitespace; output << keyword(to_string(c.class_kind())) << whitespace; if (spec) @@ -476,7 +479,7 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); if (output) { - if (func.is_friend()) + if (is_friended(func)) output << keyword("friend") << whitespace; write_storage_class(output, func.storage_class(), func.is_constexpr()); @@ -566,6 +569,8 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); if (output) { + if (is_friended(func)) + output << keyword("friend") << whitespace; if (func.is_constexpr()) output << keyword("constexpr") << whitespace; else @@ -606,6 +611,8 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(op), true); if (output) { + if (is_friended(op)) + output << keyword("friend") << whitespace; if (op.is_explicit()) output << keyword("explicit") << whitespace; if (op.is_constexpr()) @@ -630,6 +637,8 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), true); if (output) { + if (is_friended(ctor)) + output << keyword("friend") << whitespace; if (ctor.is_explicit()) output << keyword("explicit") << whitespace; if (ctor.is_constexpr()) @@ -648,6 +657,8 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), true); if (output) { + if (is_friended(dtor)) + output << keyword("friend") << whitespace; write_prefix_virtual(output, dtor.virtual_info()); output << identifier(dtor.name()) << punctuation("(") << punctuation(")"); write_noexcept(output, dtor, false); @@ -683,6 +694,24 @@ namespace } } + void generate_friend(code_generator& generator, const cpp_friend& f) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true); + if (output) + { + if (auto e = f.entity()) + generate_code(generator, e.value()); + else if (auto type = f.type()) + { + output << keyword("friend") << whitespace; + detail::write_type(output, type.value(), ""); + output << punctuation(";"); + } + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + } + } + void generate_template_type_parameter(code_generator& generator, const cpp_template_type_parameter& param) { @@ -785,7 +814,10 @@ namespace code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); if (output) { - write_template_parameters(output, func); + DEBUG_ASSERT(func.is_full_specialization(), detail::assert_handler{}); + if (!is_friended(func)) + // don't write template parameters in friend + write_template_parameters(output, func); generate_function_base(generator, func.function(), func); } } @@ -859,6 +891,8 @@ void cppast::generate_code(code_generator& generator, const cpp_entity& e) CPPAST_DETAIL_HANDLE(constructor) CPPAST_DETAIL_HANDLE(destructor) + CPPAST_DETAIL_HANDLE(friend) + CPPAST_DETAIL_HANDLE(template_type_parameter) CPPAST_DETAIL_HANDLE(non_type_template_parameter) CPPAST_DETAIL_HANDLE(template_template_parameter) diff --git a/src/cpp_entity_kind.cpp b/src/cpp_entity_kind.cpp index 74db712..6092868 100644 --- a/src/cpp_entity_kind.cpp +++ b/src/cpp_entity_kind.cpp @@ -65,6 +65,9 @@ const char* cppast::to_string(cpp_entity_kind kind) noexcept case cpp_entity_kind::destructor_t: return "destructor"; + case cpp_entity_kind::friend_t: + return "friend"; + case cpp_entity_kind::template_type_parameter_t: return "template type parameter"; case cpp_entity_kind::non_type_template_parameter_t: @@ -124,6 +127,7 @@ bool cppast::is_type(cpp_entity_kind kind) noexcept case cpp_entity_kind::conversion_op_t: case cpp_entity_kind::constructor_t: case cpp_entity_kind::destructor_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: @@ -170,6 +174,7 @@ bool cppast::is_function(cpp_entity_kind kind) noexcept case cpp_entity_kind::member_variable_t: case cpp_entity_kind::bitfield_t: case cpp_entity_kind::function_parameter_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: @@ -219,6 +224,7 @@ bool cppast::is_parameter(cpp_entity_kind kind) noexcept case cpp_entity_kind::conversion_op_t: case cpp_entity_kind::constructor_t: case cpp_entity_kind::destructor_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::alias_template_t: case cpp_entity_kind::variable_template_t: case cpp_entity_kind::function_template_t: @@ -267,6 +273,7 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept case cpp_entity_kind::conversion_op_t: case cpp_entity_kind::constructor_t: case cpp_entity_kind::destructor_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: @@ -309,6 +316,7 @@ bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept case cpp_entity_kind::conversion_op_t: case cpp_entity_kind::constructor_t: case cpp_entity_kind::destructor_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: diff --git a/src/cpp_friend.cpp b/src/cpp_friend.cpp new file mode 100644 index 0000000..2099f10 --- /dev/null +++ b/src/cpp_friend.cpp @@ -0,0 +1,26 @@ +// 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 + +using namespace cppast; + +cpp_entity_kind cpp_friend::kind() noexcept +{ + return cpp_entity_kind::friend_t; +} + +cpp_entity_kind cpp_friend::do_get_entity_kind() const noexcept +{ + return kind(); +} + +bool cppast::is_friended(const cpp_entity& e) noexcept +{ + if (is_templated(e)) + return is_friended(e.parent().value()); + return e.parent() && e.parent().value().kind() == cpp_entity_kind::friend_t; +} diff --git a/src/libclang/class_parser.cpp b/src/libclang/class_parser.cpp index dee38a2..3bf91a3 100644 --- a/src/libclang/class_parser.cpp +++ b/src/libclang/class_parser.cpp @@ -89,29 +89,40 @@ namespace } std::unique_ptr detail::parse_cpp_class(const detail::parse_context& context, - const CXCursor& cur) + const CXCursor& cur, const CXCursor& parent_cur) { - auto builder = make_class_builder(cur); - context.comments.match(builder.get(), cur); - detail::visit_children(cur, [&](const CXCursor& child) { - auto kind = clang_getCursorKind(child); - if (kind == CXCursor_CXXAccessSpecifier) - add_access_specifier(builder, child); - else if (kind == CXCursor_CXXBaseSpecifier) - 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 || clang_isExpression(kind) - || clang_isReference(kind)) - // other children due to templates and stuff - return; - else if (auto entity = parse_entity(context, child)) - builder.add_child(std::move(entity)); - }); auto is_templated = (clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound || !clang_Cursor_isNull(clang_getSpecializedCursorTemplate(cur))); - if (clang_isCursorDefinition(cur)) +#if CPPAST_CINDEX_HAS_FRIEND + auto is_friend = clang_getCursorKind(parent_cur) == CXCursor_FriendDecl; +#else + auto is_friend = false; +#endif + + auto builder = make_class_builder(cur); + if (!is_friend) + { + context.comments.match(builder.get(), cur); + detail::visit_children(cur, [&](const CXCursor& child) { + auto kind = clang_getCursorKind(child); + if (kind == CXCursor_CXXAccessSpecifier) + add_access_specifier(builder, child); + else if (kind == CXCursor_CXXBaseSpecifier) + 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 || clang_isExpression(kind) + || clang_isReference(kind)) + // other children due to templates and stuff + return; + else if (auto entity = parse_entity(context, child)) + builder.add_child(std::move(entity)); + }); + } + + if (!is_friend && clang_isCursorDefinition(cur)) return is_templated ? builder.finish() : builder.finish(*context.idx, get_entity_id(cur)); else return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) : diff --git a/src/libclang/friend_parser.cpp b/src/libclang/friend_parser.cpp new file mode 100644 index 0000000..f1ccf32 --- /dev/null +++ b/src/libclang/friend_parser.cpp @@ -0,0 +1,107 @@ +// 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 "parse_functions.hpp" +#include "libclang_visitor.hpp" + +using namespace cppast; + +#if CPPAST_CINDEX_HAS_FRIEND +std::unique_ptr detail::parse_cpp_friend(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FriendDecl, detail::assert_handler{}); + + std::string comment; + std::unique_ptr entity; + std::unique_ptr type; + std::string namespace_str; + type_safe::optional inst_builder; + detail::visit_children(cur, [&](const CXCursor& child) { + auto kind = clang_getCursorKind(child); + if (kind == CXCursor_TypeRef) + { + auto referenced = clang_getCursorReferenced(child); + if (inst_builder) + { + namespace_str.clear(); + inst_builder.value().add_argument( + detail::parse_type(context, referenced, clang_getCursorType(referenced))); + } + else if (clang_getCursorKind(referenced) == CXCursor_TemplateTypeParameter) + // parse template parameter type + type = cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(detail::get_entity_id(referenced), + detail::get_cursor_name(child).c_str())); + else if (!namespace_str.empty()) + { + // parse as user defined type + // we can't use the other branch here, + // as then the class name would be wrong + auto name = detail::get_cursor_name(referenced); + type = + cpp_user_defined_type::build(cpp_type_ref(detail::get_entity_id(referenced), + namespace_str + "::" + name.c_str())); + } + else + { + // for some reason libclang gives a type ref here + // we actually need a class decl cursor, so parse the referenced one + // this might be a definition, so give friend information to the parser + entity = parse_entity(context, referenced, cur); + comment = type_safe::copy(entity->comment()).value_or(""); + } + } + else if (kind == CXCursor_NamespaceRef) + namespace_str += detail::get_cursor_name(child).c_str(); + else if (kind == CXCursor_TemplateRef) + { + if (!namespace_str.empty()) + namespace_str += "::"; + auto templ = cpp_template_ref(detail::get_entity_id(clang_getCursorReferenced(child)), + namespace_str + detail::get_cursor_name(child).c_str()); + namespace_str.clear(); + if (!inst_builder) + inst_builder = cpp_template_instantiation_type::builder(std::move(templ)); + else + inst_builder.value().add_argument(std::move(templ)); + } + else if (clang_isDeclaration(kind)) + { + entity = parse_entity(context, child); + // steal comment + comment = type_safe::copy(entity->comment()).value_or(""); + entity->set_comment(type_safe::nullopt); + } + else if (inst_builder && clang_isExpression(kind)) + { + namespace_str.clear(); + inst_builder.value().add_argument(detail::parse_expression(context, child)); + } + }); + + std::unique_ptr result; + if (entity) + result = cpp_friend::build(std::move(entity)); + else if (type) + result = cpp_friend::build(std::move(type)); + else if (inst_builder) + result = cpp_friend::build(inst_builder.value().finish()); + else + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unknown child entity of friend declaration"); + if (!comment.empty()) + // set comment of entity... + result->set_comment(std::move(comment)); + // ... but override if this finds a different comment + // due to clang_getCursorReferenced(), this may happen + context.comments.match(*result, cur); + return result; +} +#endif diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 545d2e4..9ccc0bf 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -384,7 +384,7 @@ namespace builder.noexcept_condition(std::move(suffix.noexcept_condition)); if (is_templated_cursor(cur)) - return builder.finish(suffix.body_kind); + return builder.finish(detail::get_entity_id(cur), suffix.body_kind); else return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind); } @@ -493,7 +493,7 @@ namespace builder.virtual_info(virt.value()); if (is_templated_cursor(cur)) - return builder.finish(suffix.body_kind); + return builder.finish(detail::get_entity_id(cur), suffix.body_kind); else return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind); } @@ -618,7 +618,7 @@ std::unique_ptr detail::parse_cpp_constructor(const detail::parse_co builder.noexcept_condition(std::move(suffix.noexcept_condition)); if (is_templated_cursor(cur)) - return builder.finish(suffix.body_kind); + return builder.finish(detail::get_entity_id(cur), suffix.body_kind); else return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind); } diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 82aea52..1da4d49 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -72,7 +72,7 @@ void detail::comment_context::match(cpp_entity& e, unsigned line) const std::unique_ptr detail::parse_entity(const detail::parse_context& context, const CXCursor& cur, - const CXCursor& template_cur) try + const CXCursor& parent_cur) try { auto kind = clang_getCursorKind(cur); switch (kind) @@ -94,7 +94,7 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_TypeAliasDecl: case CXCursor_TypedefDecl: - return parse_cpp_type_alias(context, cur, template_cur); + return parse_cpp_type_alias(context, cur, parent_cur); case CXCursor_EnumDecl: return parse_cpp_enum(context, cur); case CXCursor_ClassDecl: @@ -102,7 +102,7 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_UnionDecl: if (auto spec = try_parse_full_cpp_class_template_specialization(context, cur)) return spec; - return parse_cpp_class(context, cur); + return parse_cpp_class(context, cur, parent_cur); case CXCursor_VarDecl: return parse_cpp_variable(context, cur); @@ -130,6 +130,11 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_Destructor: return parse_cpp_destructor(context, cur); +#if CPPAST_CINDEX_HAS_FRIEND + case CXCursor_FriendDecl: + return parse_cpp_friend(context, cur); +#endif + case CXCursor_TypeAliasTemplateDecl: return parse_cpp_alias_template(context, cur); case CXCursor_FunctionTemplate: diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index e2081fd..a50c49a 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -13,6 +13,12 @@ #include "parse_error.hpp" // for convenience #include "preprocessor.hpp" +#if CINDEX_VERSION_MINOR >= 36 +#define CPPAST_CINDEX_HAS_FRIEND 1 +#else +#define CPPAST_CINDEX_HAS_FRIEND 0 +#endif + namespace cppast { class cpp_expression; @@ -118,7 +124,8 @@ namespace cppast std::unique_ptr parse_cpp_enum(const parse_context& context, const CXCursor& cur); std::unique_ptr parse_cpp_class(const parse_context& context, - const CXCursor& cur); + const CXCursor& cur, + const CXCursor& parent_cur); std::unique_ptr parse_cpp_variable(const parse_context& context, const CXCursor& cur); @@ -137,6 +144,9 @@ namespace cppast std::unique_ptr parse_cpp_destructor(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_friend(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_alias_template(const parse_context& context, const CXCursor& cur); std::unique_ptr parse_cpp_function_template(const parse_context& context, @@ -146,10 +156,10 @@ namespace cppast std::unique_ptr parse_cpp_class_template_specialization( const parse_context& context, const CXCursor& cur); - // as_template: true, iff currently parsing a template + // parent_cur: used when parsing templates or friends std::unique_ptr parse_entity( const parse_context& context, const CXCursor& cur, - const CXCursor& template_cur = clang_getNullCursor()); + const CXCursor& parent_cur = clang_getNullCursor()); } } // namespace cppast::detail diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index 554ae9e..4e6ed25 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -333,7 +333,7 @@ std::unique_ptr detail::parse_cpp_class_template(const detail::parse { DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ClassTemplate, detail::assert_handler{}); - auto c = detail::parse_cpp_class(context, cur); + auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor()); if (!c) return nullptr; @@ -373,7 +373,7 @@ std::unique_ptr detail::parse_cpp_class_template_specialization( detail::assert_handler{}); auto primary = clang_getSpecializedCursorTemplate(cur); - auto c = detail::parse_cpp_class(context, cur); + auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor()); if (!c) return nullptr; diff --git a/src/visitor.cpp b/src/visitor.cpp index 86ca7c1..cd688ff 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -91,6 +91,7 @@ bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* fun case cpp_entity_kind::bitfield_t: case cpp_entity_kind::function_parameter_t: case cpp_entity_kind::destructor_t: + case cpp_entity_kind::friend_t: case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::unexposed_t: diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e070c76..fc329ee 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,6 +14,7 @@ set(tests cpp_class.cpp cpp_class_template.cpp cpp_enum.cpp + cpp_friend.cpp cpp_function.cpp cpp_function_template.cpp cpp_language_linkage.cpp diff --git a/test/cpp_friend.cpp b/test/cpp_friend.cpp new file mode 100644 index 0000000..12381c6 --- /dev/null +++ b/test/cpp_friend.cpp @@ -0,0 +1,270 @@ +// 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 +#include +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_friend", "[!hide][clang4]") +{ + auto code = R"( +/// b +class b +{ +public: + /// b::b + b(const b&) {} + + void f() const; + + /// operator int + operator int() {} +}; + +/// g +class g {}; + +namespace ns +{ + /// h + class h {}; +} + +/// b::f +void b::f() const {} + +struct foo +{ + /// friend struct a; + friend struct a; + /// friend class b; + friend class b; + + /// friend void c(int); + friend void c(int); + /// friend int d(); + friend int d(); + /// friend void e(); + friend void e() {} + + /// friend b::b(b const&); + friend b::b(const b&); + /// friend void b::f()const; + friend void b::f() const; + /// friend b::operator int(); + friend b::operator int(); + + /// friend class g; + friend g; + /// friend ns::h; + friend ns::h; +}; + +template +struct templ_a {}; + +namespace ns +{ + template class Templ> + struct templ_b {}; +} + +template +void templ_c(); + +template +struct bar +{ + /// friend T; + friend T; + /// friend templ_a; + friend templ_a; + /// friend ns::templ_b; + friend struct ns::templ_b; + + /// template + /// friend struct i; + template + friend struct i; + + /// template + /// friend void j(); + template + friend void j(); + /// template + /// friend void k(); + template + friend void k() {} + + /// friend void templ_c(); + friend void templ_c(); +}; + +/// d +int d() {} +)"; + + cpp_entity_index idx; + auto check_definition = [&](cpp_entity_id id, const char* name) { + auto def = idx.lookup_definition(id); + REQUIRE(def.has_value()); + REQUIRE(def.value().comment()); + REQUIRE(def.value().comment().value() == name); + }; + + auto file = parse(idx, "cpp_friend.cpp", code); + auto count = test_visit(*file, [&](const cpp_friend& f) { + REQUIRE(f.name().empty()); + + if (auto entity = f.entity()) + { + if (entity.value().kind() == cpp_entity_kind::class_t) + { + auto& c = static_cast(entity.value()); + REQUIRE(c.is_declaration()); + if (c.name() == "a") + REQUIRE(c.class_kind() == cpp_class_kind::struct_t); + else if (c.name() == "b") + { + REQUIRE(c.class_kind() == cpp_class_kind::class_t); + REQUIRE(c.definition()); + check_definition(c.definition().value(), "b"); + } + else if (c.name() == "g") + { + REQUIRE(c.class_kind() == cpp_class_kind::class_t); + REQUIRE(c.definition()); + check_definition(c.definition().value(), "g"); + } + else + REQUIRE(false); + } + else if (entity.value().kind() == cpp_entity_kind::class_template_t) + { + auto& c = static_cast(entity.value()); + if (c.name() == "i") + REQUIRE(c.class_().class_kind() == cpp_class_kind::struct_t); + else + REQUIRE(false); + } + else if (is_function(entity.value().kind())) + { + auto& func = static_cast(entity.value()); + if (func.name() == "c") + REQUIRE(func.is_declaration()); + else if (func.name() == "d") + { + REQUIRE(func.is_declaration()); + REQUIRE(func.definition()); + check_definition(func.definition().value(), "d"); + } + else if (func.name() == "e") + REQUIRE(func.is_definition()); + else if (func.name() == "b::b") + { + REQUIRE(func.is_declaration()); + REQUIRE(func.definition()); + check_definition(func.definition().value(), "b::b"); + } + else if (func.name() == "b::f") + { + REQUIRE(func.is_declaration()); + REQUIRE(func.definition()); + check_definition(func.definition().value(), "b::f"); + } + else if (func.name() == "b::operator int") + { + REQUIRE(func.is_declaration()); + REQUIRE(func.definition()); + check_definition(func.definition().value(), "operator int"); + } + else + REQUIRE(false); + } + else if (entity.value().kind() == cpp_entity_kind::function_template_t) + { + auto& func = static_cast(entity.value()); + if (func.name() == "j") + REQUIRE(func.function().is_declaration()); + else if (func.name() == "k") + REQUIRE(func.function().is_definition()); + else + REQUIRE(false); + } + else if (entity.value().kind() == cpp_entity_kind::function_template_specialization_t) + { + auto& func = + static_cast(entity.value()); + if (func.name() == "templ_c") + { + REQUIRE(func.function().is_declaration()); + REQUIRE(!func.arguments_exposed()); + REQUIRE(func.unexposed_arguments() == "ns::h"); + } + else + REQUIRE(false); + } + else + REQUIRE(false); + } + else if (auto type = f.type()) + { + if (type.value().kind() == cpp_type_kind::user_defined_t) + { + auto& user = static_cast(type.value()); + REQUIRE(!user.entity().is_overloaded()); + if (user.entity().name() == "ns::h") + check_definition(user.entity().id()[0u], "h"); + else + REQUIRE(false); + } + else if (type.value().kind() == cpp_type_kind::template_parameter_t) + { + auto& param = static_cast(type.value()); + REQUIRE(!param.entity().is_overloaded()); + if (param.entity().name() == "T") + REQUIRE( + equal_types(idx, param, + *cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(cpp_entity_id(""), "T")))); + else + REQUIRE(false); + } + else if (type.value().kind() == cpp_type_kind::template_instantiation_t) + { + auto& inst = static_cast(type.value()); + cpp_template_instantiation_type::builder builder( + cpp_template_ref(cpp_entity_id(""), inst.primary_template().name())); + builder.add_argument(cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(cpp_entity_id(""), "T"))); + + if (inst.primary_template().name() == "templ_a") + REQUIRE(equal_types(idx, inst, *builder.finish())); + else if (inst.primary_template().name() == "ns::templ_b") + { + builder.add_argument( + cpp_literal_expression::build(cpp_builtin_type::build(cpp_int), "0")); + builder.add_argument(cpp_template_ref(cpp_entity_id(""), "templ_a")); + REQUIRE(equal_types(idx, inst, *builder.finish())); + } + else + REQUIRE(false); + } + else + REQUIRE(false); + } + else + REQUIRE(false); + }); + REQUIRE(count == 17u); +} diff --git a/test/cpp_function.cpp b/test/cpp_function.cpp index 6869b5f..732d114 100644 --- a/test/cpp_function.cpp +++ b/test/cpp_function.cpp @@ -67,8 +67,6 @@ void ns::l() cpp_entity_index idx; auto file = parse(idx, "cpp_function.cpp", code); auto count = test_visit(*file, [&](const cpp_function& func) { - REQUIRE(!func.is_friend()); - if (func.name() == "a" || func.name() == "b" || func.name() == "c") { REQUIRE(!func.noexcept_condition()); @@ -283,5 +281,3 @@ void foo::a() {} }); REQUIRE(count == 4u); } - -// TODO: friend functions (clang 4.0 required)