Add and parse cpp_friend

Only works in clang>=4.
This commit is contained in:
Jonathan Müller 2017-04-06 11:45:17 +02:00
commit c3d1d97892
18 changed files with 629 additions and 92 deletions

View file

@ -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,

View file

@ -0,0 +1,76 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// 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 <type_safe/optional.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_type.hpp>
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<cpp_friend, cpp_entity>
{
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<cpp_friend> build(std::unique_ptr<cpp_entity> e)
{
return std::unique_ptr<cpp_friend>(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<cpp_friend> build(std::unique_ptr<cpp_type> type)
{
return std::unique_ptr<cpp_friend>(new cpp_friend(std::move(type)));
}
/// \returns An optional reference to the entity it declares as friend, or `nullptr`.
type_safe::optional_ref<const cpp_entity> 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<const cpp_type> type() const noexcept
{
return type_safe::opt_ref(type_.get());
}
private:
cpp_friend(std::unique_ptr<cpp_entity> e) : cpp_entity("")
{
add_child(std::move(e));
}
cpp_friend(std::unique_ptr<cpp_type> type) : cpp_entity(""), type_(std::move(type))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> type_;
friend cpp_entity_container<cpp_friend, cpp_entity>;
};
/// \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

View file

@ -147,8 +147,10 @@ namespace cppast
/// \returns The finished function without registering it.
/// \notes This is intended for templated functions only.
std::unique_ptr<T> finish(cpp_function_body_kind body_kind)
std::unique_ptr<T> 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<cpp_type> return_type_;
cpp_storage_class_specifiers storage_;
bool friend_;
bool constexpr_;
};
} // namespace cppast

View file

@ -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;

View file

@ -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

View file

@ -10,6 +10,7 @@
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_enum.hpp>
#include <cppast/cpp_file.hpp>
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_function.hpp>
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_language_linkage.hpp>
@ -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)

View file

@ -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:

26
src/cpp_friend.cpp Normal file
View file

@ -0,0 +1,26 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_entity_kind.hpp>
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;
}

View file

@ -89,29 +89,40 @@ namespace
}
std::unique_ptr<cpp_entity> 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)) :

View file

@ -0,0 +1,107 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_template.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp"
using namespace cppast;
#if CPPAST_CINDEX_HAS_FRIEND
std::unique_ptr<cpp_entity> 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<cpp_entity> entity;
std::unique_ptr<cpp_type> type;
std::string namespace_str;
type_safe::optional<cpp_template_instantiation_type::builder> 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<cpp_entity> 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

View file

@ -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<cpp_entity> 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);
}

View file

@ -72,7 +72,7 @@ void detail::comment_context::match(cpp_entity& e, unsigned line) const
std::unique_ptr<cpp_entity> 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<cpp_entity> 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<cpp_entity> 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<cpp_entity> 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:

View file

@ -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<cpp_entity> parse_cpp_enum(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur,
const CXCursor& parent_cur);
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
@ -137,6 +144,9 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_friend(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function_template(const parse_context& context,
@ -146,10 +156,10 @@ namespace cppast
std::unique_ptr<cpp_entity> 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<cpp_entity> parse_entity(
const parse_context& context, const CXCursor& cur,
const CXCursor& template_cur = clang_getNullCursor());
const CXCursor& parent_cur = clang_getNullCursor());
}
} // namespace cppast::detail

View file

@ -333,7 +333,7 @@ std::unique_ptr<cpp_entity> 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<cpp_entity> 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;

View file

@ -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:

View file

@ -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

270
test/cpp_friend.cpp Normal file
View file

@ -0,0 +1,270 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_function.hpp>
#include <cppast/cpp_function_template.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#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 <typename T>
struct templ_a {};
namespace ns
{
template <typename T, int I, template <typename> class Templ>
struct templ_b {};
}
template <typename T>
void templ_c();
template <typename T>
struct bar
{
/// friend T;
friend T;
/// friend templ_a<T>;
friend templ_a<T>;
/// friend ns::templ_b<T,0,templ_a>;
friend struct ns::templ_b<T, 0, templ_a>;
/// template<typename U>
/// friend struct i;
template <typename U>
friend struct i;
/// template<typename U>
/// friend void j();
template <typename U>
friend void j();
/// template<typename U>
/// friend void k();
template <typename U>
friend void k() {}
/// friend void templ_c<ns::h>();
friend void templ_c<ns::h>();
};
/// 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<cpp_friend>(*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<const cpp_class&>(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<const cpp_class_template&>(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<const cpp_function_base&>(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<const cpp_function_template&>(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<const cpp_function_template_specialization&>(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<const cpp_user_defined_type&>(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<const cpp_template_parameter_type&>(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<const cpp_template_instantiation_type&>(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);
}

View file

@ -67,8 +67,6 @@ void ns::l()
cpp_entity_index idx;
auto file = parse(idx, "cpp_function.cpp", code);
auto count = test_visit<cpp_function>(*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)