// Copyright (C) 2017-2019 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 "libclang_visitor.hpp" #include "parse_functions.hpp" using namespace cppast; 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, nullptr, 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, nullptr, child, cur); if (entity) { // 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; }