Update clang-format

This commit is contained in:
Jonathan Müller 2018-09-20 19:57:03 +02:00
commit 9f18b7f4d8
89 changed files with 11377 additions and 11546 deletions

File diff suppressed because it is too large Load diff

View file

@ -12,32 +12,32 @@ using namespace cppast;
namespace
{
const char* get_attribute_name(cpp_attribute_kind kind) noexcept
const char* get_attribute_name(cpp_attribute_kind kind) noexcept
{
switch (kind)
{
switch (kind)
{
case cpp_attribute_kind::alignas_:
return "alignas";
case cpp_attribute_kind::carries_dependency:
return "carries_dependency";
case cpp_attribute_kind::deprecated:
return "deprecated";
case cpp_attribute_kind::fallthrough:
return "fallthrough";
case cpp_attribute_kind::maybe_unused:
return "maybe_unused";
case cpp_attribute_kind::nodiscard:
return "nodiscard";
case cpp_attribute_kind::noreturn:
return "noreturn";
case cpp_attribute_kind::alignas_:
return "alignas";
case cpp_attribute_kind::carries_dependency:
return "carries_dependency";
case cpp_attribute_kind::deprecated:
return "deprecated";
case cpp_attribute_kind::fallthrough:
return "fallthrough";
case cpp_attribute_kind::maybe_unused:
return "maybe_unused";
case cpp_attribute_kind::nodiscard:
return "nodiscard";
case cpp_attribute_kind::noreturn:
return "noreturn";
case cpp_attribute_kind::unknown:
return "unknown";
}
return "<error>";
case cpp_attribute_kind::unknown:
return "unknown";
}
return "<error>";
}
} // namespace
cpp_attribute::cpp_attribute(cpp_attribute_kind kind,
type_safe::optional<cpp_token_string> arguments)
@ -49,13 +49,13 @@ cpp_attribute::cpp_attribute(cpp_attribute_kind kind,
type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
const cpp_attribute_list& attributes, const std::string& name)
{
auto iter =
std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) {
if (attribute.scope())
return attribute.scope().value() + "::" + attribute.name() == name;
else
return attribute.name() == name;
});
auto iter
= std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) {
if (attribute.scope())
return attribute.scope().value() + "::" + attribute.name() == name;
else
return attribute.name() == name;
});
if (iter == attributes.end())
return nullptr;
@ -66,9 +66,9 @@ type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
const cpp_attribute_list& attributes, cpp_attribute_kind kind)
{
auto iter =
std::find_if(attributes.begin(), attributes.end(),
[&](const cpp_attribute& attribute) { return attribute.kind() == kind; });
auto iter
= std::find_if(attributes.begin(), attributes.end(),
[&](const cpp_attribute& attribute) { return attribute.kind() == kind; });
if (iter == attributes.end())
return nullptr;

View file

@ -4,10 +4,10 @@
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_kind.hpp>
using namespace cppast;
@ -103,70 +103,69 @@ cpp_entity_kind cpp_class::do_get_entity_kind() const noexcept
namespace
{
cpp_entity_ref get_type_ref(const cpp_type& type)
cpp_entity_ref get_type_ref(const cpp_type& type)
{
if (type.kind() == cpp_type_kind::user_defined_t)
{
if (type.kind() == cpp_type_kind::user_defined_t)
{
auto& ref = static_cast<const cpp_user_defined_type&>(type).entity();
return cpp_entity_ref(ref.id()[0u], ref.name());
}
else if (type.kind() == cpp_type_kind::template_instantiation_t)
{
auto& ref =
static_cast<const cpp_template_instantiation_type&>(type).primary_template();
return cpp_entity_ref(ref.id()[0u], ref.name());
}
DEBUG_ASSERT(type.kind() == cpp_type_kind::template_parameter_t
|| type.kind() == cpp_type_kind::decltype_t
|| type.kind() == cpp_type_kind::decltype_auto_t
|| type.kind() == cpp_type_kind::unexposed_t,
detail::assert_handler{});
return cpp_entity_ref(cpp_entity_id("<null id>"), "");
auto& ref = static_cast<const cpp_user_defined_type&>(type).entity();
return cpp_entity_ref(ref.id()[0u], ref.name());
}
else if (type.kind() == cpp_type_kind::template_instantiation_t)
{
auto& ref = static_cast<const cpp_template_instantiation_type&>(type).primary_template();
return cpp_entity_ref(ref.id()[0u], ref.name());
}
type_safe::optional_ref<const cpp_entity> get_entity_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
{
auto result = ref.get(index);
if (result.empty())
return nullptr;
DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{});
DEBUG_ASSERT(type.kind() == cpp_type_kind::template_parameter_t
|| type.kind() == cpp_type_kind::decltype_t
|| type.kind() == cpp_type_kind::decltype_auto_t
|| type.kind() == cpp_type_kind::unexposed_t,
detail::assert_handler{});
return cpp_entity_ref(cpp_entity_id("<null id>"), "");
}
auto entity = result.front();
if (entity->kind() == cpp_class_template::kind())
return type_safe::ref(static_cast<const cpp_class_template&>(*entity).class_());
else if (entity->kind() == cpp_class_template_specialization::kind())
return type_safe::ref(
static_cast<const cpp_class_template_specialization&>(*entity).class_());
else
return entity;
type_safe::optional_ref<const cpp_entity> get_entity_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
{
auto result = ref.get(index);
if (result.empty())
return nullptr;
DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{});
auto entity = result.front();
if (entity->kind() == cpp_class_template::kind())
return type_safe::ref(static_cast<const cpp_class_template&>(*entity).class_());
else if (entity->kind() == cpp_class_template_specialization::kind())
return type_safe::ref(
static_cast<const cpp_class_template_specialization&>(*entity).class_());
else
return entity;
}
type_safe::optional_ref<const cpp_class> get_class_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
{
auto entity = get_entity_impl(index, ref);
if (!entity)
return nullptr;
if (entity.value().kind() == cpp_alias_template::kind())
{
auto& alias = static_cast<const cppast::cpp_alias_template&>(entity.value());
return get_class_impl(index, get_type_ref(alias.type_alias().underlying_type()));
}
type_safe::optional_ref<const cpp_class> get_class_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
else if (entity.value().kind() == cpp_type_alias::kind())
{
auto entity = get_entity_impl(index, ref);
if (!entity)
return nullptr;
if (entity.value().kind() == cpp_alias_template::kind())
{
auto& alias = static_cast<const cppast::cpp_alias_template&>(entity.value());
return get_class_impl(index, get_type_ref(alias.type_alias().underlying_type()));
}
else if (entity.value().kind() == cpp_type_alias::kind())
{
auto& alias = static_cast<const cppast::cpp_type_alias&>(entity.value());
return get_class_impl(index, get_type_ref(alias.underlying_type()));
}
else
{
DEBUG_ASSERT(entity.value().kind() == cpp_class::kind(), detail::assert_handler{});
return type_safe::ref(static_cast<const cpp_class&>(entity.value()));
}
auto& alias = static_cast<const cppast::cpp_type_alias&>(entity.value());
return get_class_impl(index, get_type_ref(alias.underlying_type()));
}
else
{
DEBUG_ASSERT(entity.value().kind() == cpp_class::kind(), detail::assert_handler{});
return type_safe::ref(static_cast<const cpp_class&>(entity.value()));
}
}
} // namespace
type_safe::optional_ref<const cpp_class> cppast::get_class(const cpp_entity_index& index,
const cpp_base_class& base)

View file

@ -4,17 +4,16 @@
#include <cppast/cpp_entity_index.hpp>
#include <cppast/detail/assert.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_file.hpp>
#include <cppast/detail/assert.hpp>
using namespace cppast;
cpp_entity_index::duplicate_definition_error::duplicate_definition_error()
: std::logic_error("duplicate registration of entity definition")
{
}
{}
void cpp_entity_index::register_definition(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const

View file

@ -17,8 +17,8 @@ std::unique_ptr<cpp_enum_value> cpp_enum_value::build(const cpp_entity_index& id
std::string name,
std::unique_ptr<cpp_expression> value)
{
auto result =
std::unique_ptr<cpp_enum_value>(new cpp_enum_value(std::move(name), std::move(value)));
auto result
= std::unique_ptr<cpp_enum_value>(new cpp_enum_value(std::move(name), std::move(value)));
idx.register_definition(std::move(id), type_safe::ref(*result));
return result;
}

View file

@ -8,77 +8,77 @@ using namespace cppast;
namespace
{
void write_literal(code_generator::output& output, const cpp_literal_expression& expr)
void write_literal(code_generator::output& output, const cpp_literal_expression& expr)
{
auto type_kind = cpp_void;
if (expr.type().kind() == cpp_type_kind::builtin_t)
type_kind = static_cast<const cpp_builtin_type&>(expr.type()).builtin_type_kind();
else if (expr.type().kind() == cpp_type_kind::pointer_t)
{
auto type_kind = cpp_void;
if (expr.type().kind() == cpp_type_kind::builtin_t)
type_kind = static_cast<const cpp_builtin_type&>(expr.type()).builtin_type_kind();
else if (expr.type().kind() == cpp_type_kind::pointer_t)
auto& pointee = static_cast<const cpp_pointer_type&>(expr.type()).pointee();
if (pointee.kind() == cpp_type_kind::builtin_t)
{
auto& pointee = static_cast<const cpp_pointer_type&>(expr.type()).pointee();
if (pointee.kind() == cpp_type_kind::builtin_t)
{
auto& builtin_pointee = static_cast<const cpp_builtin_type&>(pointee);
if (builtin_pointee.builtin_type_kind() == cpp_char
|| builtin_pointee.builtin_type_kind() == cpp_wchar
|| builtin_pointee.builtin_type_kind() == cpp_char16
|| builtin_pointee.builtin_type_kind() == cpp_char32)
// pointer to char aka string
type_kind = builtin_pointee.builtin_type_kind();
}
}
switch (type_kind)
{
case cpp_void:
output << token_seq(expr.value());
break;
case cpp_bool:
output << keyword(expr.value());
break;
case cpp_uchar:
case cpp_ushort:
case cpp_uint:
case cpp_ulong:
case cpp_ulonglong:
case cpp_uint128:
case cpp_schar:
case cpp_short:
case cpp_int:
case cpp_long:
case cpp_longlong:
case cpp_int128:
output << int_literal(expr.value());
break;
case cpp_float:
case cpp_double:
case cpp_longdouble:
case cpp_float128:
output << float_literal(expr.value());
break;
case cpp_char:
case cpp_wchar:
case cpp_char16:
case cpp_char32:
output << string_literal(expr.value());
break;
case cpp_nullptr:
output << keyword(expr.value());
break;
auto& builtin_pointee = static_cast<const cpp_builtin_type&>(pointee);
if (builtin_pointee.builtin_type_kind() == cpp_char
|| builtin_pointee.builtin_type_kind() == cpp_wchar
|| builtin_pointee.builtin_type_kind() == cpp_char16
|| builtin_pointee.builtin_type_kind() == cpp_char32)
// pointer to char aka string
type_kind = builtin_pointee.builtin_type_kind();
}
}
void write_unexposed(code_generator::output& output, const cpp_unexposed_expression& expr)
switch (type_kind)
{
detail::write_token_string(output, expr.expression());
case cpp_void:
output << token_seq(expr.value());
break;
case cpp_bool:
output << keyword(expr.value());
break;
case cpp_uchar:
case cpp_ushort:
case cpp_uint:
case cpp_ulong:
case cpp_ulonglong:
case cpp_uint128:
case cpp_schar:
case cpp_short:
case cpp_int:
case cpp_long:
case cpp_longlong:
case cpp_int128:
output << int_literal(expr.value());
break;
case cpp_float:
case cpp_double:
case cpp_longdouble:
case cpp_float128:
output << float_literal(expr.value());
break;
case cpp_char:
case cpp_wchar:
case cpp_char16:
case cpp_char32:
output << string_literal(expr.value());
break;
case cpp_nullptr:
output << keyword(expr.value());
break;
}
}
void write_unexposed(code_generator::output& output, const cpp_unexposed_expression& expr)
{
detail::write_token_string(output, expr.expression());
}
} // namespace
void detail::write_expression(code_generator::output& output, const cpp_expression& expr)
{
switch (expr.kind())

View file

@ -15,73 +15,73 @@ using namespace cppast;
namespace
{
type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_entity& e)
type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_entity& e)
{
switch (e.kind())
{
switch (e.kind())
{
case cpp_entity_kind::enum_t:
return type_safe::ref(static_cast<const cpp_enum&>(e));
case cpp_entity_kind::class_t:
return type_safe::ref(static_cast<const cpp_class&>(e));
case cpp_entity_kind::variable_t:
return type_safe::ref(static_cast<const cpp_variable&>(e));
case cpp_entity_kind::function_t:
case cpp_entity_kind::member_function_t:
case cpp_entity_kind::conversion_op_t:
case cpp_entity_kind::constructor_t:
case cpp_entity_kind::destructor_t:
return type_safe::ref(static_cast<const cpp_function_base&>(e));
case cpp_entity_kind::function_template_t:
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
return get_declarable(*static_cast<const cpp_template&>(e).begin());
case cpp_entity_kind::enum_t:
return type_safe::ref(static_cast<const cpp_enum&>(e));
case cpp_entity_kind::class_t:
return type_safe::ref(static_cast<const cpp_class&>(e));
case cpp_entity_kind::variable_t:
return type_safe::ref(static_cast<const cpp_variable&>(e));
case cpp_entity_kind::function_t:
case cpp_entity_kind::member_function_t:
case cpp_entity_kind::conversion_op_t:
case cpp_entity_kind::constructor_t:
case cpp_entity_kind::destructor_t:
return type_safe::ref(static_cast<const cpp_function_base&>(e));
case cpp_entity_kind::function_template_t:
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
return get_declarable(*static_cast<const cpp_template&>(e).begin());
case cpp_entity_kind::file_t:
case cpp_entity_kind::macro_parameter_t:
case cpp_entity_kind::macro_definition_t:
case cpp_entity_kind::include_directive_t:
case cpp_entity_kind::language_linkage_t:
case cpp_entity_kind::namespace_t:
case cpp_entity_kind::namespace_alias_t:
case cpp_entity_kind::using_directive_t:
case cpp_entity_kind::using_declaration_t:
case cpp_entity_kind::type_alias_t:
case cpp_entity_kind::enum_value_t:
case cpp_entity_kind::access_specifier_t:
case cpp_entity_kind::base_class_t:
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:
case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
return nullptr;
case cpp_entity_kind::count:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
case cpp_entity_kind::file_t:
case cpp_entity_kind::macro_parameter_t:
case cpp_entity_kind::macro_definition_t:
case cpp_entity_kind::include_directive_t:
case cpp_entity_kind::language_linkage_t:
case cpp_entity_kind::namespace_t:
case cpp_entity_kind::namespace_alias_t:
case cpp_entity_kind::using_directive_t:
case cpp_entity_kind::using_declaration_t:
case cpp_entity_kind::type_alias_t:
case cpp_entity_kind::enum_value_t:
case cpp_entity_kind::access_specifier_t:
case cpp_entity_kind::base_class_t:
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:
case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
return nullptr;
case cpp_entity_kind::count:
break;
}
type_safe::optional_ref<const cpp_entity> get_definition_impl(const cpp_entity_index& idx,
const cpp_entity& e)
{
auto declarable = get_declarable(e);
if (!declarable || declarable.value().is_definition())
// not declarable or is a definition
// return reference to entity itself
return type_safe::ref(e);
// else lookup definition
return idx.lookup_definition(declarable.value().definition().value());
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return nullptr;
}
type_safe::optional_ref<const cpp_entity> get_definition_impl(const cpp_entity_index& idx,
const cpp_entity& e)
{
auto declarable = get_declarable(e);
if (!declarable || declarable.value().is_definition())
// not declarable or is a definition
// return reference to entity itself
return type_safe::ref(e);
// else lookup definition
return idx.lookup_definition(declarable.value().definition().value());
}
} // namespace
bool cppast::is_definition(const cpp_entity& e) noexcept

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_function_type.hpp>
#include <cppast/cpp_type_alias.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_type_alias.hpp>
using namespace cppast;
@ -174,36 +174,36 @@ std::unique_ptr<cpp_dependent_type> cpp_dependent_type::build(
namespace
{
// is directly a complex type
// is_complex_type also checks for children
bool is_direct_complex(const cpp_type& type) noexcept
// is directly a complex type
// is_complex_type also checks for children
bool is_direct_complex(const cpp_type& type) noexcept
{
switch (type.kind())
{
switch (type.kind())
{
case cpp_type_kind::builtin_t:
case cpp_type_kind::user_defined_t:
case cpp_type_kind::auto_t:
case cpp_type_kind::decltype_t:
case cpp_type_kind::decltype_auto_t:
case cpp_type_kind::cv_qualified_t:
case cpp_type_kind::pointer_t:
case cpp_type_kind::reference_t:
case cpp_type_kind::template_parameter_t:
case cpp_type_kind::template_instantiation_t:
case cpp_type_kind::dependent_t:
case cpp_type_kind::unexposed_t:
return false;
case cpp_type_kind::array_t:
case cpp_type_kind::function_t:
case cpp_type_kind::member_function_t:
case cpp_type_kind::member_object_t:
return true;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
case cpp_type_kind::builtin_t:
case cpp_type_kind::user_defined_t:
case cpp_type_kind::auto_t:
case cpp_type_kind::decltype_t:
case cpp_type_kind::decltype_auto_t:
case cpp_type_kind::cv_qualified_t:
case cpp_type_kind::pointer_t:
case cpp_type_kind::reference_t:
case cpp_type_kind::template_parameter_t:
case cpp_type_kind::template_instantiation_t:
case cpp_type_kind::dependent_t:
case cpp_type_kind::unexposed_t:
return false;
case cpp_type_kind::array_t:
case cpp_type_kind::function_t:
case cpp_type_kind::member_function_t:
case cpp_type_kind::member_object_t:
return true;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return false;
}
} // namespace
bool detail::is_complex_type(const cpp_type& type) noexcept
@ -226,281 +226,278 @@ bool detail::is_complex_type(const cpp_type& type) noexcept
namespace
{
void comma(const code_generator::output& output)
{
output << punctuation(",");
if (output.formatting().is_set(formatting_flags::comma_ws))
output << whitespace;
}
void comma(const code_generator::output& output)
{
output << punctuation(",");
if (output.formatting().is_set(formatting_flags::comma_ws))
output << whitespace;
}
void bracket_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::bracket_ws))
output << whitespace;
}
void bracket_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::bracket_ws))
output << whitespace;
}
void operator_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::operator_ws))
output << whitespace;
}
void operator_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::operator_ws))
output << whitespace;
}
void write_builtin(code_generator::output& output, const cpp_builtin_type& type)
{
output << keyword(to_string(type.builtin_type_kind()));
}
void write_builtin(code_generator::output& output, const cpp_builtin_type& type)
{
output << keyword(to_string(type.builtin_type_kind()));
}
void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type)
{
output << type.entity();
}
void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type)
{
output << type.entity();
}
void write_auto(code_generator::output& output, const cpp_auto_type&)
{
output << keyword("auto");
}
void write_auto(code_generator::output& output, const cpp_auto_type&)
{
output << keyword("auto");
}
void write_decltype(code_generator::output& output, const cpp_decltype_type& type)
{
output << keyword("decltype") << punctuation("(") << bracket_ws;
detail::write_expression(output, type.expression());
output << bracket_ws << punctuation(")");
}
void write_decltype(code_generator::output& output, const cpp_decltype_type& type)
{
output << keyword("decltype") << punctuation("(") << bracket_ws;
detail::write_expression(output, type.expression());
output << bracket_ws << punctuation(")");
}
void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&)
{
output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto")
<< bracket_ws << punctuation(")");
}
void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&)
{
output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto") << bracket_ws
<< punctuation(")");
}
void write_cv_qualified_prefix(code_generator::output& output,
const cpp_cv_qualified_type& type)
{
detail::write_type_prefix(output, type.type());
void write_cv_qualified_prefix(code_generator::output& output, const cpp_cv_qualified_type& type)
{
detail::write_type_prefix(output, type.type());
if (is_direct_complex(type.type()))
output << punctuation("(") << bracket_ws;
if (is_const(type.cv_qualifier()))
output << whitespace << keyword("const");
if (is_volatile(type.cv_qualifier()))
output << whitespace << keyword("volatile");
}
void write_cv_qualified_suffix(code_generator::output& output,
const cpp_cv_qualified_type& type)
{
if (is_direct_complex(type.type()))
output << bracket_ws << punctuation(")");
detail::write_type_suffix(output, type.type());
}
bool pointer_requires_paren(const cpp_pointer_type& type)
{
auto kind = type.pointee().kind();
return kind == cpp_type_kind::function_t || kind == cpp_type_kind::array_t;
}
void write_pointer_prefix(code_generator::output& output, const cpp_pointer_type& type)
{
detail::write_type_prefix(output, type.pointee());
if (pointer_requires_paren(type))
output << punctuation("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
output << punctuation("*");
}
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
{
if (pointer_requires_paren(type))
output << bracket_ws << punctuation(")");
detail::write_type_suffix(output, type.pointee());
}
void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type)
{
detail::write_type_prefix(output, type.referee());
if (is_direct_complex(type.referee()))
output << punctuation("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
if (type.reference_kind() == cpp_ref_lvalue)
output << punctuation("&");
else if (type.reference_kind() == cpp_ref_rvalue)
output << punctuation("&&");
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
void write_reference_suffix(code_generator::output& output, const cpp_reference_type& type)
{
if (is_direct_complex(type.referee()))
output << bracket_ws << punctuation(")");
detail::write_type_suffix(output, type.referee());
}
void write_array_prefix(code_generator::output& output, const cpp_array_type& type)
{
detail::write_type_prefix(output, type.value_type());
}
void write_array_suffix(code_generator::output& output, const cpp_array_type& type)
{
output << punctuation("[");
if (type.size())
{
output << bracket_ws;
detail::write_expression(output, type.size().value());
output << bracket_ws;
}
output << punctuation("]");
detail::write_type_suffix(output, type.value_type());
}
void write_function_prefix(code_generator::output& output, const cpp_function_type& type)
{
detail::write_type_prefix(output, type.return_type());
}
template <typename T>
void write_parameters(code_generator::output& output, const T& type)
{
if (is_direct_complex(type.type()))
output << punctuation("(") << bracket_ws;
auto need_sep = false;
for (auto& param : type.parameter_types())
{
if (need_sep)
output << comma;
else
need_sep = true;
detail::write_type_prefix(output, param);
detail::write_type_suffix(output, param);
}
if (type.is_variadic())
{
if (need_sep)
output << comma;
output << punctuation("...");
}
if (is_const(type.cv_qualifier()))
output << whitespace << keyword("const");
if (is_volatile(type.cv_qualifier()))
output << whitespace << keyword("volatile");
}
void write_cv_qualified_suffix(code_generator::output& output, const cpp_cv_qualified_type& type)
{
if (is_direct_complex(type.type()))
output << bracket_ws << punctuation(")");
}
detail::write_type_suffix(output, type.type());
}
void write_function_suffix(code_generator::output& output, const cpp_function_type& type)
{
write_parameters(output, type);
bool pointer_requires_paren(const cpp_pointer_type& type)
{
auto kind = type.pointee().kind();
return kind == cpp_type_kind::function_t || kind == cpp_type_kind::array_t;
}
detail::write_type_suffix(output, type.return_type());
}
const cpp_type& strip_class_type(const cpp_type& type, cpp_cv* cv, cpp_reference* ref)
{
if (type.kind() == cpp_type_kind::cv_qualified_t)
{
auto& cv_qual = static_cast<const cpp_cv_qualified_type&>(type);
if (cv)
*cv = cv_qual.cv_qualifier();
return strip_class_type(cv_qual.type(), cv, ref);
}
else if (type.kind() == cpp_type_kind::reference_t)
{
auto& ref_type = static_cast<const cpp_reference_type&>(type);
if (ref)
*ref = ref_type.reference_kind();
return strip_class_type(ref_type.referee(), cv, ref);
}
else
{
DEBUG_ASSERT(!detail::is_complex_type(type), detail::assert_handler{});
return type;
}
}
void write_member_function_prefix(code_generator::output& output,
const cpp_member_function_type& type)
{
detail::write_type_prefix(output, type.return_type());
void write_pointer_prefix(code_generator::output& output, const cpp_pointer_type& type)
{
detail::write_type_prefix(output, type.pointee());
if (pointer_requires_paren(type))
output << punctuation("(") << bracket_ws;
detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr));
output << punctuation("::");
}
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
void write_member_function_suffix(code_generator::output& output,
const cpp_member_function_type& type)
{
output << punctuation("*");
}
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
{
if (pointer_requires_paren(type))
output << bracket_ws << punctuation(")");
write_parameters(output, type);
detail::write_type_suffix(output, type.pointee());
}
auto cv = cpp_cv_none;
auto ref = cpp_ref_none;
strip_class_type(type.class_type(), &cv, &ref);
void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type)
{
detail::write_type_prefix(output, type.referee());
if (cv == cpp_cv_const_volatile)
output << keyword("const") << whitespace << keyword("volatile");
else if (is_const(cv))
output << keyword("const");
else if (is_volatile(cv))
output << keyword("volatile");
if (ref == cpp_ref_lvalue)
output << operator_ws << punctuation("&") << operator_ws;
else if (ref == cpp_ref_rvalue)
output << operator_ws << punctuation("&&") << operator_ws;
detail::write_type_suffix(output, type.return_type());
}
void write_member_object_prefix(code_generator::output& output,
const cpp_member_object_type& type)
{
detail::write_type_prefix(output, type.object_type());
if (is_direct_complex(type.referee()))
output << punctuation("(") << bracket_ws;
DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{});
detail::write_type_prefix(output, type.class_type());
output << punctuation("::");
}
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&)
{
if (type.reference_kind() == cpp_ref_lvalue)
output << punctuation("&");
else if (type.reference_kind() == cpp_ref_rvalue)
output << punctuation("&&");
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
void write_reference_suffix(code_generator::output& output, const cpp_reference_type& type)
{
if (is_direct_complex(type.referee()))
output << bracket_ws << punctuation(")");
}
detail::write_type_suffix(output, type.referee());
}
void write_template_parameter(code_generator::output& output,
const cpp_template_parameter_type& type)
void write_array_prefix(code_generator::output& output, const cpp_array_type& type)
{
detail::write_type_prefix(output, type.value_type());
}
void write_array_suffix(code_generator::output& output, const cpp_array_type& type)
{
output << punctuation("[");
if (type.size())
{
output << type.entity();
output << bracket_ws;
detail::write_expression(output, type.size().value());
output << bracket_ws;
}
output << punctuation("]");
detail::write_type_suffix(output, type.value_type());
}
void write_template_instantiation(code_generator::output& output,
const cpp_template_instantiation_type& type)
void write_function_prefix(code_generator::output& output, const cpp_function_type& type)
{
detail::write_type_prefix(output, type.return_type());
}
template <typename T>
void write_parameters(code_generator::output& output, const T& type)
{
output << punctuation("(") << bracket_ws;
auto need_sep = false;
for (auto& param : type.parameter_types())
{
output << type.primary_template();
if (output.was_reference_excluded())
return;
if (type.arguments_exposed())
detail::write_template_arguments(output, type.arguments());
if (need_sep)
output << comma;
else
output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments())
<< bracket_ws << punctuation(">");
need_sep = true;
detail::write_type_prefix(output, param);
detail::write_type_suffix(output, param);
}
if (type.is_variadic())
{
if (need_sep)
output << comma;
output << punctuation("...");
}
void write_dependent(code_generator::output& output, const cpp_dependent_type& type)
{
output << token_seq(type.name());
}
output << bracket_ws << punctuation(")");
}
void write_unexposed(code_generator::output& output, const cpp_unexposed_type& type)
void write_function_suffix(code_generator::output& output, const cpp_function_type& type)
{
write_parameters(output, type);
detail::write_type_suffix(output, type.return_type());
}
const cpp_type& strip_class_type(const cpp_type& type, cpp_cv* cv, cpp_reference* ref)
{
if (type.kind() == cpp_type_kind::cv_qualified_t)
{
output << token_seq(type.name());
auto& cv_qual = static_cast<const cpp_cv_qualified_type&>(type);
if (cv)
*cv = cv_qual.cv_qualifier();
return strip_class_type(cv_qual.type(), cv, ref);
}
else if (type.kind() == cpp_type_kind::reference_t)
{
auto& ref_type = static_cast<const cpp_reference_type&>(type);
if (ref)
*ref = ref_type.reference_kind();
return strip_class_type(ref_type.referee(), cv, ref);
}
else
{
DEBUG_ASSERT(!detail::is_complex_type(type), detail::assert_handler{});
return type;
}
}
void write_member_function_prefix(code_generator::output& output,
const cpp_member_function_type& type)
{
detail::write_type_prefix(output, type.return_type());
output << punctuation("(") << bracket_ws;
detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr));
output << punctuation("::");
}
void write_member_function_suffix(code_generator::output& output,
const cpp_member_function_type& type)
{
output << bracket_ws << punctuation(")");
write_parameters(output, type);
auto cv = cpp_cv_none;
auto ref = cpp_ref_none;
strip_class_type(type.class_type(), &cv, &ref);
if (cv == cpp_cv_const_volatile)
output << keyword("const") << whitespace << keyword("volatile");
else if (is_const(cv))
output << keyword("const");
else if (is_volatile(cv))
output << keyword("volatile");
if (ref == cpp_ref_lvalue)
output << operator_ws << punctuation("&") << operator_ws;
else if (ref == cpp_ref_rvalue)
output << operator_ws << punctuation("&&") << operator_ws;
detail::write_type_suffix(output, type.return_type());
}
void write_member_object_prefix(code_generator::output& output, const cpp_member_object_type& type)
{
detail::write_type_prefix(output, type.object_type());
output << punctuation("(") << bracket_ws;
DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{});
detail::write_type_prefix(output, type.class_type());
output << punctuation("::");
}
void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&)
{
output << bracket_ws << punctuation(")");
}
void write_template_parameter(code_generator::output& output,
const cpp_template_parameter_type& type)
{
output << type.entity();
}
void write_template_instantiation(code_generator::output& output,
const cpp_template_instantiation_type& type)
{
output << type.primary_template();
if (output.was_reference_excluded())
return;
if (type.arguments_exposed())
detail::write_template_arguments(output, type.arguments());
else
output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments())
<< bracket_ws << punctuation(">");
}
void write_dependent(code_generator::output& output, const cpp_dependent_type& type)
{
output << token_seq(type.name());
}
void write_unexposed(code_generator::output& output, const cpp_unexposed_type& type)
{
output << token_seq(type.name());
}
} // namespace
void detail::write_type_prefix(code_generator::output& output, const cpp_type& type)

View file

@ -2,8 +2,8 @@
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_class.hpp>
#include <clang-c/Index.h>
#include <cppast/cpp_class.hpp>
#include "libclang_visitor.hpp"
#include "parse_functions.hpp"
@ -12,104 +12,104 @@ using namespace cppast;
namespace
{
cpp_class_kind parse_class_kind(detail::cxtoken_stream& stream)
cpp_class_kind parse_class_kind(detail::cxtoken_stream& stream)
{
auto kind = clang_getTemplateCursorKind(stream.cursor());
if (kind == CXCursor_NoDeclFound)
kind = clang_getCursorKind(stream.cursor());
if (detail::skip_if(stream, "template"))
// skip template parameters
detail::skip_brackets(stream);
detail::skip_if(stream, "friend");
if (detail::skip_if(stream, "extern"))
// extern template
detail::skip(stream, "template");
switch (kind)
{
auto kind = clang_getTemplateCursorKind(stream.cursor());
if (kind == CXCursor_NoDeclFound)
kind = clang_getCursorKind(stream.cursor());
if (detail::skip_if(stream, "template"))
// skip template parameters
detail::skip_brackets(stream);
detail::skip_if(stream, "friend");
if (detail::skip_if(stream, "extern"))
// extern template
detail::skip(stream, "template");
switch (kind)
{
case CXCursor_ClassDecl:
detail::skip(stream, "class");
return cpp_class_kind::class_t;
case CXCursor_StructDecl:
detail::skip(stream, "struct");
return cpp_class_kind::struct_t;
case CXCursor_UnionDecl:
detail::skip(stream, "union");
return cpp_class_kind::union_t;
default:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
case CXCursor_ClassDecl:
detail::skip(stream, "class");
return cpp_class_kind::class_t;
case CXCursor_StructDecl:
detail::skip(stream, "struct");
return cpp_class_kind::struct_t;
case CXCursor_UnionDecl:
detail::skip(stream, "union");
return cpp_class_kind::union_t;
default:
break;
}
cpp_class::builder make_class_builder(const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto kind = parse_class_kind(stream);
auto attributes = detail::parse_attributes(stream);
auto name = detail::get_cursor_name(cur);
auto result = cpp_class::builder(name.c_str(), kind);
result.get().add_attribute(attributes);
return result;
}
cpp_access_specifier_kind convert_access(const CXCursor& cur)
{
switch (clang_getCXXAccessSpecifier(cur))
{
case CX_CXXInvalidAccessSpecifier:
break;
case CX_CXXPublic:
return cpp_public;
case CX_CXXProtected:
return cpp_protected;
case CX_CXXPrivate:
return cpp_private;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_public;
}
void add_access_specifier(cpp_class::builder& builder, const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXAccessSpecifier, detail::assert_handler{});
builder.access_specifier(convert_access(cur));
}
void add_base_class(cpp_class::builder& builder, const detail::parse_context& context,
const CXCursor& cur, const CXCursor& class_cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{});
auto access = convert_access(cur);
auto is_virtual = clang_isVirtualBase(cur) != 0u;
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [<attribute>] [virtual] [<access>] <name>
// can't use spelling to get the name
auto attributes = detail::parse_attributes(stream);
if (is_virtual)
detail::skip(stream, "virtual");
detail::skip_if(stream, to_string(access));
auto name = detail::to_string(stream, stream.end()).as_string();
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);
base.add_attribute(attributes);
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_class_kind::class_t;
}
cpp_class::builder make_class_builder(const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto kind = parse_class_kind(stream);
auto attributes = detail::parse_attributes(stream);
auto name = detail::get_cursor_name(cur);
auto result = cpp_class::builder(name.c_str(), kind);
result.get().add_attribute(attributes);
return result;
}
cpp_access_specifier_kind convert_access(const CXCursor& cur)
{
switch (clang_getCXXAccessSpecifier(cur))
{
case CX_CXXInvalidAccessSpecifier:
break;
case CX_CXXPublic:
return cpp_public;
case CX_CXXProtected:
return cpp_protected;
case CX_CXXPrivate:
return cpp_private;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_public;
}
void add_access_specifier(cpp_class::builder& builder, const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXAccessSpecifier, detail::assert_handler{});
builder.access_specifier(convert_access(cur));
}
void add_base_class(cpp_class::builder& builder, const detail::parse_context& context,
const CXCursor& cur, const CXCursor& class_cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{});
auto access = convert_access(cur);
auto is_virtual = clang_isVirtualBase(cur) != 0u;
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [<attribute>] [virtual] [<access>] <name>
// can't use spelling to get the name
auto attributes = detail::parse_attributes(stream);
if (is_virtual)
detail::skip(stream, "virtual");
detail::skip_if(stream, to_string(access));
auto name = detail::to_string(stream, stream.end()).as_string();
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);
base.add_attribute(attributes);
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context& context,
const CXCursor& cur, const CXCursor& parent_cur)
{
@ -145,9 +145,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
stream.bump();
}
if (!scope.empty())
semantic_parent =
cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
semantic_parent
= cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
}
context.comments.match(builder.get(), cur);
@ -159,12 +159,12 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
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 || kind == CXCursor_ParmDecl
|| clang_isExpression(kind) || clang_isReference(kind)
|| kind
== CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows
else if (kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl
|| clang_isExpression(kind) || clang_isReference(kind)
|| kind == CXCursor_UnexposedAttr) // I have no idea what this is, but happens
// on Windows
// other children due to templates and stuff
return;
else if (auto entity = parse_entity(context, &builder.get(), child))
@ -173,10 +173,10 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
}
if (!is_friend && clang_isCursorDefinition(cur))
return is_templated ?
builder.finish(std::move(semantic_parent)) :
builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent));
return is_templated
? builder.finish(std::move(semantic_parent))
: builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent));
else
return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) :
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));
}

View file

@ -13,290 +13,282 @@ using namespace cppast;
detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token)
: value_(clang_getTokenSpelling(tu_unit, token)), kind_(clang_getTokenKind(token))
{
}
{}
namespace
{
bool cursor_is_function(CXCursorKind kind)
bool cursor_is_function(CXCursorKind kind)
{
return kind == CXCursor_FunctionDecl || kind == CXCursor_CXXMethod
|| kind == CXCursor_Constructor || kind == CXCursor_Destructor
|| kind == CXCursor_ConversionFunction;
}
CXSourceLocation get_next_location(const CXTranslationUnit& tu, CXFile file,
const CXSourceLocation& loc, int inc = 1)
{
unsigned offset;
clang_getSpellingLocation(loc, nullptr, nullptr, nullptr, &offset);
if (inc >= 0)
offset += unsigned(inc);
else
offset -= unsigned(-inc);
return clang_getLocationForOffset(tu, file, offset);
}
class simple_tokenizer
{
public:
explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range) : tu_(tu)
{
return kind == CXCursor_FunctionDecl || kind == CXCursor_CXXMethod
|| kind == CXCursor_Constructor || kind == CXCursor_Destructor
|| kind == CXCursor_ConversionFunction;
clang_tokenize(tu, range, &tokens_, &no_);
}
CXSourceLocation get_next_location(const CXTranslationUnit& tu, CXFile file,
const CXSourceLocation& loc, int inc = 1)
~simple_tokenizer()
{
unsigned offset;
clang_getSpellingLocation(loc, nullptr, nullptr, nullptr, &offset);
if (inc >= 0)
offset += unsigned(inc);
else
offset -= unsigned(-inc);
return clang_getLocationForOffset(tu, file, offset);
clang_disposeTokens(tu_, tokens_, no_);
}
class simple_tokenizer
simple_tokenizer(const simple_tokenizer&) = delete;
simple_tokenizer& operator=(const simple_tokenizer&) = delete;
unsigned size() const noexcept
{
public:
explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range) : tu_(tu)
{
clang_tokenize(tu, range, &tokens_, &no_);
}
~simple_tokenizer()
{
clang_disposeTokens(tu_, tokens_, no_);
}
simple_tokenizer(const simple_tokenizer&) = delete;
simple_tokenizer& operator=(const simple_tokenizer&) = delete;
unsigned size() const noexcept
{
return no_;
}
const CXToken& operator[](unsigned i) const noexcept
{
return tokens_[i];
}
private:
CXTranslationUnit tu_;
CXToken* tokens_;
unsigned no_;
};
bool token_after_is(const CXTranslationUnit& tu, const CXFile& file,
const CXSourceLocation& loc, const char* token_str, int inc)
{
auto loc_after = get_next_location(tu, file, loc, inc);
if (!clang_Location_isFromMainFile(loc_after))
return false;
simple_tokenizer tokenizer(tu, inc > 0 ? clang_getRange(loc, loc_after) :
clang_getRange(loc_after, loc));
if (tokenizer.size() == 0u)
return false;
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
return spelling == token_str;
return no_;
}
// clang_getCursorExtent() is somehow broken in various ways
// this function returns the actual CXSourceRange that covers all parts required for parsing
// might include more tokens
// this function is the reason you shouldn't use libclang
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
bool& unmunch)
const CXToken& operator[](unsigned i) const noexcept
{
unmunch = false;
return tokens_[i];
}
auto extent = clang_getCursorExtent(cur);
auto begin = clang_getRangeStart(extent);
auto end = clang_getRangeEnd(extent);
private:
CXTranslationUnit tu_;
CXToken* tokens_;
unsigned no_;
};
auto kind = clang_getCursorKind(cur);
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur))
|| kind == CXCursor_VarDecl || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|| kind == CXCursor_NonTypeTemplateParameter)
bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, const CXSourceLocation& loc,
const char* token_str, int inc)
{
auto loc_after = get_next_location(tu, file, loc, inc);
if (!clang_Location_isFromMainFile(loc_after))
return false;
simple_tokenizer tokenizer(tu, inc > 0 ? clang_getRange(loc, loc_after)
: clang_getRange(loc_after, loc));
if (tokenizer.size() == 0u)
return false;
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
return spelling == token_str;
}
// clang_getCursorExtent() is somehow broken in various ways
// this function returns the actual CXSourceRange that covers all parts required for parsing
// might include more tokens
// this function is the reason you shouldn't use libclang
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
bool& unmunch)
{
unmunch = false;
auto extent = clang_getCursorExtent(cur);
auto begin = clang_getRangeStart(extent);
auto end = clang_getRangeEnd(extent);
auto kind = clang_getCursorKind(cur);
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur))
|| kind == CXCursor_VarDecl || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|| kind == CXCursor_NonTypeTemplateParameter)
{
if (token_after_is(tu, file, begin, "]", -2) && token_after_is(tu, file, begin, "]", -3))
{
if (token_after_is(tu, file, begin, "]", -2)
&& token_after_is(tu, file, begin, "]", -3))
{
while (!token_after_is(tu, file, begin, "[", -1)
&& !token_after_is(tu, file, begin, "[", -2))
begin = get_next_location(tu, file, begin, -1);
begin = get_next_location(tu, file, begin, -3);
DEBUG_ASSERT(token_after_is(tu, file, begin, "[", 0)
&& token_after_is(tu, file, get_next_location(tu, file, begin),
"[", 0),
detail::parse_error_handler{}, cur,
"error in pre-function attribute parsing");
}
else if (token_after_is(tu, file, begin, ")", -2))
{
// maybe alignas specifier
auto save_begin = begin;
auto paren_count = 1;
begin = get_next_location(tu, file, begin, -1);
for (auto last_begin = begin; paren_count != 0; last_begin = begin)
{
begin = get_next_location(tu, file, begin, -1);
if (token_after_is(tu, file, begin, "(", -1))
--paren_count;
else if (token_after_is(tu, file, begin, ")", -1))
++paren_count;
DEBUG_ASSERT(!clang_equalLocations(last_begin, begin),
detail::parse_error_handler{}, cur,
"infinite loop in alignas parsing");
}
begin = get_next_location(tu, file, begin, -(int(std::strlen("alignas")) + 1));
if (token_after_is(tu, file, begin, "alignas", 0))
begin = get_next_location(tu, file, begin, -1);
else
begin = save_begin;
}
}
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
{
auto is_definition = false;
// if a function we need to remove the body
// it does not need to be parsed
detail::visit_children(cur, [&](const CXCursor& child) {
if (clang_getCursorKind(child) == CXCursor_CompoundStmt
|| clang_getCursorKind(child) == CXCursor_CXXTryStmt
|| clang_getCursorKind(child) == CXCursor_InitListExpr)
{
auto child_extent = clang_getCursorExtent(child);
end = clang_getRangeStart(child_extent);
is_definition = true;
}
});
if (!is_definition)
{
// i have no idea why this is necessary
is_definition = token_after_is(tu, file, end, "{", 0)
|| token_after_is(tu, file, end, "try", 0)
|| token_after_is(tu, file, end, ":", 0);
if (is_definition)
// need to extend range here to include the token
end = get_next_location(tu, file, end);
}
if (!is_definition && !token_after_is(tu, file, end, ";", 0))
{
// we do not have a body, but it is not a declaration either
do
{
end = get_next_location(tu, file, end);
} while (!token_after_is(tu, file, end, ";", 0));
}
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
while (!token_after_is(tu, file, begin, "[", -1)
&& !token_after_is(tu, file, begin, "[", -2))
begin = get_next_location(tu, file, begin, -1);
else if (kind == CXCursor_Destructor && token_after_is(tu, file, end, ")", 0))
// necessary for some other reason
end = get_next_location(tu, file, end);
begin = get_next_location(tu, file, begin, -3);
DEBUG_ASSERT(token_after_is(tu, file, begin, "[", 0)
&& token_after_is(tu, file, get_next_location(tu, file, begin), "[",
0),
detail::parse_error_handler{}, cur,
"error in pre-function attribute parsing");
}
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, end, "(", 0))
else if (token_after_is(tu, file, begin, ")", -2))
{
// if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters
auto next = get_next_location(tu, file, end);
auto prev = end;
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next))
// maybe alignas specifier
auto save_begin = begin;
auto paren_count = 1;
begin = get_next_location(tu, file, begin, -1);
for (auto last_begin = begin; paren_count != 0; last_begin = begin)
{
if (token_after_is(tu, file, next, "(", 0))
++paren_count;
else if (token_after_is(tu, file, next, ")", 0))
begin = get_next_location(tu, file, begin, -1);
if (token_after_is(tu, file, begin, "(", -1))
--paren_count;
prev = next;
else if (token_after_is(tu, file, begin, ")", -1))
++paren_count;
DEBUG_ASSERT(!clang_equalLocations(last_begin, begin),
detail::parse_error_handler{}, cur,
"infinite loop in alignas parsing");
}
#if CINDEX_VERSION_MINOR < 37
end = prev;
#else
end = next;
#endif
}
else if (kind == CXCursor_TemplateTemplateParameter
&& token_after_is(tu, file, end, "<", 0))
{
// if you have a template template parameter in a template template parameter,
// the tokens are all messed up, only contain the `template`
begin = get_next_location(tu, file, begin, -(int(std::strlen("alignas")) + 1));
// first: skip to closing angle bracket
// luckily no need to handle expressions here
auto next = get_next_location(tu, file, end, 2);
for (auto angle_count = 1; angle_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, next, ">", 0))
--angle_count;
else if (token_after_is(tu, file, next, ">>", 0))
angle_count -= 2;
else if (token_after_is(tu, file, next, "<", 0))
++angle_count;
}
// second: skip until end of parameter
// no need to handle default, so look for '>' or ','
while (!token_after_is(tu, file, next, ">", 0)
&& !token_after_is(tu, file, next, ",", 0))
next = get_next_location(tu, file, next);
// now we found the proper end of the token
end = get_next_location(tu, file, next, -1);
if (token_after_is(tu, file, begin, "alignas", 0))
begin = get_next_location(tu, file, begin, -1);
else
begin = save_begin;
}
else if ((kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& token_after_is(tu, file, end, "...", 0))
{
// variadic tokens in unnamed parameter not included
end = get_next_location(tu, file, end, 3);
if (token_after_is(tu, file, end, ".", 0))
// extra whitespace, so bump again
// this should all go away once I redid the whole token thing...
end = get_next_location(tu, file, end, 1);
DEBUG_ASSERT(token_after_is(tu, file, end, ">", 0)
|| token_after_is(tu, file, end, ",", 0),
detail::parse_error_handler{}, cur,
"unexpected token in variadic parameter workaround");
}
else if ((kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& !token_after_is(tu, file, end, ">", 0)
&& !token_after_is(tu, file, end, ",", 0))
{
DEBUG_ASSERT(token_after_is(tu, file, get_next_location(tu, file, end, -2), ">>", 0),
detail::parse_error_handler{}, cur,
"unexpected token in maximal munch workaround");
unmunch = true;
// need to shrink range anyway
end = get_next_location(tu, file, end, -1);
}
else if (kind == CXCursor_EnumDecl && !token_after_is(tu, file, end, ";", 0))
{
while (!token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, end, ",", 0))
{
// need to support attributes
// just give up and extend the range to the range of the entire enum...
auto parent = clang_getCursorLexicalParent(cur);
end = clang_getRangeEnd(clang_getCursorExtent(parent));
}
else if (kind == CXCursor_ParmDecl && !token_after_is(tu, file, end, "]", -1))
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_FieldDecl || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter
#if CINDEX_VERSION_MINOR < 37
|| clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier
|| kind == CXCursor_TemplateTypeParameter
#endif
)
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_UnexposedDecl)
{
// include semicolon, if necessary
if (token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
return clang_getRange(begin, end);
}
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
{
auto is_definition = false;
// if a function we need to remove the body
// it does not need to be parsed
detail::visit_children(cur, [&](const CXCursor& child) {
if (clang_getCursorKind(child) == CXCursor_CompoundStmt
|| clang_getCursorKind(child) == CXCursor_CXXTryStmt
|| clang_getCursorKind(child) == CXCursor_InitListExpr)
{
auto child_extent = clang_getCursorExtent(child);
end = clang_getRangeStart(child_extent);
is_definition = true;
}
});
if (!is_definition)
{
// i have no idea why this is necessary
is_definition = token_after_is(tu, file, end, "{", 0)
|| token_after_is(tu, file, end, "try", 0)
|| token_after_is(tu, file, end, ":", 0);
if (is_definition)
// need to extend range here to include the token
end = get_next_location(tu, file, end);
}
if (!is_definition && !token_after_is(tu, file, end, ";", 0))
{
// we do not have a body, but it is not a declaration either
do
{
end = get_next_location(tu, file, end);
} while (!token_after_is(tu, file, end, ";", 0));
}
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
begin = get_next_location(tu, file, begin, -1);
else if (kind == CXCursor_Destructor && token_after_is(tu, file, end, ")", 0))
// necessary for some other reason
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, end, "(", 0))
{
// if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters
auto next = get_next_location(tu, file, end);
auto prev = end;
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, next, "(", 0))
++paren_count;
else if (token_after_is(tu, file, next, ")", 0))
--paren_count;
prev = next;
}
#if CINDEX_VERSION_MINOR < 37
end = prev;
#else
end = next;
#endif
}
else if (kind == CXCursor_TemplateTemplateParameter && token_after_is(tu, file, end, "<", 0))
{
// if you have a template template parameter in a template template parameter,
// the tokens are all messed up, only contain the `template`
// first: skip to closing angle bracket
// luckily no need to handle expressions here
auto next = get_next_location(tu, file, end, 2);
for (auto angle_count = 1; angle_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, next, ">", 0))
--angle_count;
else if (token_after_is(tu, file, next, ">>", 0))
angle_count -= 2;
else if (token_after_is(tu, file, next, "<", 0))
++angle_count;
}
// second: skip until end of parameter
// no need to handle default, so look for '>' or ','
while (!token_after_is(tu, file, next, ">", 0) && !token_after_is(tu, file, next, ",", 0))
next = get_next_location(tu, file, next);
// now we found the proper end of the token
end = get_next_location(tu, file, next, -1);
}
else if ((kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& token_after_is(tu, file, end, "...", 0))
{
// variadic tokens in unnamed parameter not included
end = get_next_location(tu, file, end, 3);
if (token_after_is(tu, file, end, ".", 0))
// extra whitespace, so bump again
// this should all go away once I redid the whole token thing...
end = get_next_location(tu, file, end, 1);
DEBUG_ASSERT(token_after_is(tu, file, end, ">", 0) || token_after_is(tu, file, end, ",", 0),
detail::parse_error_handler{}, cur,
"unexpected token in variadic parameter workaround");
}
else if ((kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& !token_after_is(tu, file, end, ">", 0) && !token_after_is(tu, file, end, ",", 0))
{
DEBUG_ASSERT(token_after_is(tu, file, get_next_location(tu, file, end, -2), ">>", 0),
detail::parse_error_handler{}, cur,
"unexpected token in maximal munch workaround");
unmunch = true;
// need to shrink range anyway
end = get_next_location(tu, file, end, -1);
}
else if (kind == CXCursor_EnumDecl && !token_after_is(tu, file, end, ";", 0))
{
while (!token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, end, ",", 0))
{
// need to support attributes
// just give up and extend the range to the range of the entire enum...
auto parent = clang_getCursorLexicalParent(cur);
end = clang_getRangeEnd(clang_getCursorExtent(parent));
}
else if (kind == CXCursor_ParmDecl && !token_after_is(tu, file, end, "]", -1))
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_FieldDecl || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter
#if CINDEX_VERSION_MINOR < 37
|| clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier
|| kind == CXCursor_TemplateTypeParameter
#endif
)
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_UnexposedDecl)
{
// include semicolon, if necessary
if (token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
return clang_getRange(begin, end);
}
} // namespace
detail::cxtokenizer::cxtokenizer(const CXTranslationUnit& tu, const CXFile& file,
@ -326,15 +318,15 @@ void detail::skip(detail::cxtoken_stream& stream, const char* str)
namespace
{
bool starts_with(const char*& str, const detail::cxtoken& t)
{
if (std::strncmp(str, t.c_str(), t.value().length()) != 0)
return false;
str += t.value().length();
while (*str == ' ' || *str == '\t')
++str;
return true;
}
bool starts_with(const char*& str, const detail::cxtoken& t)
{
if (std::strncmp(str, t.c_str(), t.value().length()) != 0)
return false;
str += t.value().length();
while (*str == ' ' || *str == '\t')
++str;
return true;
}
} // namespace
bool detail::skip_if(detail::cxtoken_stream& stream, const char* str, bool multi_token)
@ -359,16 +351,16 @@ bool detail::skip_if(detail::cxtoken_stream& stream, const char* str, bool multi
namespace
{
// whether or not the current angle bracket can be a comparison
// note: this is a heuristic I hope works often enough
bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind)
{
if (cur == "<")
return last_kind == CXToken_Literal;
else if (cur == ">")
return next_kind == CXToken_Literal;
return false;
}
// whether or not the current angle bracket can be a comparison
// note: this is a heuristic I hope works often enough
bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind)
{
if (cur == "<")
return last_kind == CXToken_Literal;
else if (cur == ">")
return next_kind == CXToken_Literal;
return false;
}
} // namespace
detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream stream)
@ -430,161 +422,160 @@ void detail::skip_brackets(detail::cxtoken_stream& stream)
namespace
{
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
{
// using identifier :
if (skip_if(stream, "using"))
{
// using identifier :
if (skip_if(stream, "using"))
{
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{},
stream.cursor(), "expected identifier");
auto scope = stream.get().value().std_str();
skip(stream, ":");
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{},
stream.cursor(), "expected identifier");
auto scope = stream.get().value().std_str();
skip(stream, ":");
return scope;
}
else
return type_safe::nullopt;
return scope;
}
else
return type_safe::nullopt;
}
cpp_attribute_kind get_attribute_kind(const std::string& name)
cpp_attribute_kind get_attribute_kind(const std::string& name)
{
if (name == "carries_dependency")
return cpp_attribute_kind::carries_dependency;
else if (name == "deprecated")
return cpp_attribute_kind::deprecated;
else if (name == "fallthrough")
return cpp_attribute_kind::fallthrough;
else if (name == "maybe_unused")
return cpp_attribute_kind::maybe_unused;
else if (name == "nodiscard")
return cpp_attribute_kind::nodiscard;
else if (name == "noreturn")
return cpp_attribute_kind::noreturn;
else
return cpp_attribute_kind::unknown;
}
cpp_token_string parse_attribute_arguments(detail::cxtoken_stream& stream)
{
auto end = find_closing_bracket(stream);
skip(stream, "(");
auto arguments = detail::to_string(stream, end);
stream.set_cur(end);
skip(stream, ")");
return arguments;
}
cpp_attribute parse_attribute_token(detail::cxtoken_stream& stream,
type_safe::optional<std::string> scope)
{
// (identifier ::)_opt identifier ( '(' some tokens ')' )_opt ..._opt
// parse name
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier");
auto name = stream.get().value().std_str();
if (skip_if(stream, "::"))
{
if (name == "carries_dependency")
return cpp_attribute_kind::carries_dependency;
else if (name == "deprecated")
return cpp_attribute_kind::deprecated;
else if (name == "fallthrough")
return cpp_attribute_kind::fallthrough;
else if (name == "maybe_unused")
return cpp_attribute_kind::maybe_unused;
else if (name == "nodiscard")
return cpp_attribute_kind::nodiscard;
else if (name == "noreturn")
return cpp_attribute_kind::noreturn;
else
return cpp_attribute_kind::unknown;
}
// name was actually a scope, so parse name again
DEBUG_ASSERT(!scope, detail::parse_error_handler{}, stream.cursor(),
"attribute using + scope not allowed");
scope = std::move(name);
cpp_token_string parse_attribute_arguments(detail::cxtoken_stream& stream)
{
auto end = find_closing_bracket(stream);
skip(stream, "(");
auto arguments = detail::to_string(stream, end);
stream.set_cur(end);
skip(stream, ")");
return arguments;
}
cpp_attribute parse_attribute_token(detail::cxtoken_stream& stream,
type_safe::optional<std::string> scope)
{
// (identifier ::)_opt identifier ( '(' some tokens ')' )_opt ..._opt
// parse name
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier");
auto name = stream.get().value().std_str();
if (skip_if(stream, "::"))
{
// name was actually a scope, so parse name again
DEBUG_ASSERT(!scope, detail::parse_error_handler{}, stream.cursor(),
"attribute using + scope not allowed");
scope = std::move(name);
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier");
name = stream.get().value().std_str();
}
// parse arguments
type_safe::optional<cpp_token_string> arguments;
if (stream.peek() == "(")
arguments = parse_attribute_arguments(stream);
// parse variadic token
auto is_variadic = skip_if(stream, "...");
// get kind
auto kind = get_attribute_kind(name);
if (!scope && kind != cpp_attribute_kind::unknown)
return cpp_attribute(kind, std::move(arguments));
else
return cpp_attribute(std::move(scope), std::move(name), std::move(arguments),
is_variadic);
name = stream.get().value().std_str();
}
bool parse_attribute_impl(cpp_attribute_list& result, detail::cxtoken_stream& stream)
// parse arguments
type_safe::optional<cpp_token_string> arguments;
if (stream.peek() == "(")
arguments = parse_attribute_arguments(stream);
// parse variadic token
auto is_variadic = skip_if(stream, "...");
// get kind
auto kind = get_attribute_kind(name);
if (!scope && kind != cpp_attribute_kind::unknown)
return cpp_attribute(kind, std::move(arguments));
else
return cpp_attribute(std::move(scope), std::move(name), std::move(arguments), is_variadic);
}
bool parse_attribute_impl(cpp_attribute_list& result, detail::cxtoken_stream& stream)
{
if (skip_if(stream, "[") && stream.peek() == "[")
{
if (skip_if(stream, "[") && stream.peek() == "[")
// C++11 attribute
// [[<attribute>]]
// ^
skip(stream, "[");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, "]"))
{
// C++11 attribute
// [[<attribute>]]
// ^
skip(stream, "[");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, "]"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
// [[<attribute>]]
// ^
skip(stream, "]");
return true;
}
else if (skip_if(stream, "alignas"))
{
// alignas specifier
// alignas(<some arguments>)
// ^
auto arguments = parse_attribute_arguments(stream);
result.push_back(cpp_attribute(cpp_attribute_kind::alignas_, std::move(arguments)));
}
else if (skip_if(stream, "__attribute__") && stream.peek() == "(")
{
// GCC/clang attributes
// __attribute__((<attribute>))
// ^^
skip(stream, "(");
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
skip(stream, ")");
return true;
}
else if (skip_if(stream, "__declspec"))
{
// MSVC declspec
// __declspec(<attribute>)
// ^
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
return true;
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
return false;
// [[<attribute>]]
// ^
skip(stream, "]");
return true;
}
else if (skip_if(stream, "alignas"))
{
// alignas specifier
// alignas(<some arguments>)
// ^
auto arguments = parse_attribute_arguments(stream);
result.push_back(cpp_attribute(cpp_attribute_kind::alignas_, std::move(arguments)));
}
else if (skip_if(stream, "__attribute__") && stream.peek() == "(")
{
// GCC/clang attributes
// __attribute__((<attribute>))
// ^^
skip(stream, "(");
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
skip(stream, ")");
return true;
}
else if (skip_if(stream, "__declspec"))
{
// MSVC declspec
// __declspec(<attribute>)
// ^
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
return true;
}
return false;
}
} // namespace
cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream, bool skip_anway)
@ -602,37 +593,37 @@ cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream, bool
namespace
{
cpp_token_kind get_kind(const detail::cxtoken& token)
cpp_token_kind get_kind(const detail::cxtoken& token)
{
switch (token.kind())
{
switch (token.kind())
{
case CXToken_Punctuation:
return cpp_token_kind::punctuation;
case CXToken_Keyword:
return cpp_token_kind::keyword;
case CXToken_Identifier:
return cpp_token_kind::identifier;
case CXToken_Literal:
{
auto spelling = token.value().std_str();
if (spelling.find('.') != std::string::npos)
return cpp_token_kind::float_literal;
else if (std::isdigit(spelling.front()))
return cpp_token_kind::int_literal;
else if (spelling.back() == '\'')
return cpp_token_kind::char_literal;
else
return cpp_token_kind::string_literal;
}
case CXToken_Comment:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
case CXToken_Punctuation:
return cpp_token_kind::punctuation;
case CXToken_Keyword:
return cpp_token_kind::keyword;
case CXToken_Identifier:
return cpp_token_kind::identifier;
case CXToken_Literal:
{
auto spelling = token.value().std_str();
if (spelling.find('.') != std::string::npos)
return cpp_token_kind::float_literal;
else if (std::isdigit(spelling.front()))
return cpp_token_kind::int_literal;
else if (spelling.back() == '\'')
return cpp_token_kind::char_literal;
else
return cpp_token_kind::string_literal;
}
case CXToken_Comment:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_token_kind::punctuation;
}
} // namespace
cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end)

View file

@ -15,194 +15,189 @@
namespace cppast
{
namespace detail
namespace detail
{
class cxtoken
{
class cxtoken
public:
explicit cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token);
const cxstring& value() const noexcept
{
public:
explicit cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token);
const cxstring& value() const noexcept
{
return value_;
}
const char* c_str() const noexcept
{
return value_.c_str();
}
CXTokenKind kind() const noexcept
{
return kind_;
}
private:
cxstring value_;
CXTokenKind kind_;
};
inline bool operator==(const cxtoken& tok, const char* str) noexcept
{
return tok.value() == str;
return value_;
}
inline bool operator==(const char* str, const cxtoken& tok) noexcept
const char* c_str() const noexcept
{
return str == tok.value();
return value_.c_str();
}
inline bool operator!=(const cxtoken& tok, const char* str) noexcept
CXTokenKind kind() const noexcept
{
return !(tok == str);
return kind_;
}
inline bool operator!=(const char* str, const cxtoken& tok) noexcept
{
return !(str == tok);
}
private:
cxstring value_;
CXTokenKind kind_;
};
using cxtoken_iterator = std::vector<cxtoken>::const_iterator;
class cxtokenizer
{
public:
explicit cxtokenizer(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur);
cxtoken_iterator begin() const noexcept
{
return tokens_.begin();
}
cxtoken_iterator end() const noexcept
{
return tokens_.end();
}
// if it returns true, the last token is ">>",
// but should haven been ">"
// only a problem for template parameters
bool unmunch() const noexcept
{
return unmunch_;
}
private:
std::vector<cxtoken> tokens_;
bool unmunch_;
};
class cxtoken_stream
{
public:
explicit cxtoken_stream(const cxtokenizer& tokenizer, const CXCursor& cur)
: cursor_(cur),
begin_(tokenizer.begin()),
cur_(begin_),
end_(tokenizer.end()),
unmunch_(tokenizer.unmunch())
{
}
const cxtoken& peek() const noexcept
{
if (done())
return *std::prev(end_);
return *cur_;
}
void bump() noexcept
{
if (cur_ != end_)
++cur_;
}
void bump_back() noexcept
{
if (cur_ != begin_)
--cur_;
}
const cxtoken& get() noexcept
{
auto& result = peek();
bump();
return result;
}
bool done() const noexcept
{
return cur_ == end_;
}
const CXCursor& cursor() const noexcept
{
return cursor_;
}
cxtoken_iterator begin() const noexcept
{
return begin_;
}
cxtoken_iterator cur() const noexcept
{
return cur_;
}
cxtoken_iterator end() const noexcept
{
return end_;
}
void set_cur(cxtoken_iterator iter) noexcept
{
cur_ = iter;
}
bool unmunch() const noexcept
{
return unmunch_;
}
private:
CXCursor cursor_;
cxtoken_iterator begin_, cur_, end_;
bool unmunch_;
};
// skips the next token
// asserts that it has the given string
void skip(cxtoken_stream& stream, const char* str);
// skips the next token if it has the given string
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false);
// returns the location of the closing bracket
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
cxtoken_iterator find_closing_bracket(cxtoken_stream stream);
// skips brackets
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
void skip_brackets(cxtoken_stream& stream);
// parses attributes
// if skip_anyway is true it will bump even if no attributes have been parsed
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
// converts a token range to a string
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
// appends token to scope, if it is still valid
// else clears it
// note: does not consume the token if it is not valid,
// returns false in that case
bool append_scope(cxtoken_stream& stream, std::string& scope);
inline bool operator==(const cxtoken& tok, const char* str) noexcept
{
return tok.value() == str;
}
} // namespace cppast::detail
inline bool operator==(const char* str, const cxtoken& tok) noexcept
{
return str == tok.value();
}
inline bool operator!=(const cxtoken& tok, const char* str) noexcept
{
return !(tok == str);
}
inline bool operator!=(const char* str, const cxtoken& tok) noexcept
{
return !(str == tok);
}
using cxtoken_iterator = std::vector<cxtoken>::const_iterator;
class cxtokenizer
{
public:
explicit cxtokenizer(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur);
cxtoken_iterator begin() const noexcept
{
return tokens_.begin();
}
cxtoken_iterator end() const noexcept
{
return tokens_.end();
}
// if it returns true, the last token is ">>",
// but should haven been ">"
// only a problem for template parameters
bool unmunch() const noexcept
{
return unmunch_;
}
private:
std::vector<cxtoken> tokens_;
bool unmunch_;
};
class cxtoken_stream
{
public:
explicit cxtoken_stream(const cxtokenizer& tokenizer, const CXCursor& cur)
: cursor_(cur), begin_(tokenizer.begin()), cur_(begin_), end_(tokenizer.end()),
unmunch_(tokenizer.unmunch())
{}
const cxtoken& peek() const noexcept
{
if (done())
return *std::prev(end_);
return *cur_;
}
void bump() noexcept
{
if (cur_ != end_)
++cur_;
}
void bump_back() noexcept
{
if (cur_ != begin_)
--cur_;
}
const cxtoken& get() noexcept
{
auto& result = peek();
bump();
return result;
}
bool done() const noexcept
{
return cur_ == end_;
}
const CXCursor& cursor() const noexcept
{
return cursor_;
}
cxtoken_iterator begin() const noexcept
{
return begin_;
}
cxtoken_iterator cur() const noexcept
{
return cur_;
}
cxtoken_iterator end() const noexcept
{
return end_;
}
void set_cur(cxtoken_iterator iter) noexcept
{
cur_ = iter;
}
bool unmunch() const noexcept
{
return unmunch_;
}
private:
CXCursor cursor_;
cxtoken_iterator begin_, cur_, end_;
bool unmunch_;
};
// skips the next token
// asserts that it has the given string
void skip(cxtoken_stream& stream, const char* str);
// skips the next token if it has the given string
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false);
// returns the location of the closing bracket
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
cxtoken_iterator find_closing_bracket(cxtoken_stream stream);
// skips brackets
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
void skip_brackets(cxtoken_stream& stream);
// parses attributes
// if skip_anyway is true it will bump even if no attributes have been parsed
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
// converts a token range to a string
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
// appends token to scope, if it is still valid
// else clears it
// note: does not consume the token if it is not valid,
// returns false in that case
bool append_scope(cxtoken_stream& stream, std::string& scope);
} // namespace detail
} // namespace cppast
#endif // CPPAST_CXTOKENIZER_HPP_INCLUDED

View file

@ -28,7 +28,7 @@ detail::cxstring detail::get_type_kind_spelling(const CXType& type) noexcept
namespace
{
std::mutex mtx;
std::mutex mtx;
}
void detail::print_cursor_info(const CXCursor& cur) noexcept

View file

@ -11,21 +11,21 @@
namespace cppast
{
namespace detail
{
cxstring get_display_name(const CXCursor& cur) noexcept;
namespace detail
{
cxstring get_display_name(const CXCursor& cur) noexcept;
cxstring get_cursor_kind_spelling(const CXCursor& cur) noexcept;
cxstring get_cursor_kind_spelling(const CXCursor& cur) noexcept;
cxstring get_type_kind_spelling(const CXType& type) noexcept;
cxstring get_type_kind_spelling(const CXType& type) noexcept;
void print_cursor_info(const CXCursor& cur) noexcept;
void print_cursor_info(const CXCursor& cur) noexcept;
void print_type_info(const CXType& type) noexcept;
void print_type_info(const CXType& type) noexcept;
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept;
}
} // namespace cppast::detail
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept;
} // namespace detail
} // namespace cppast
#endif // CPPAST_DEBUG_HELPER_HPP_INCLUDED

View file

@ -4,80 +4,77 @@
#include <cppast/cpp_enum.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast;
namespace
{
std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context,
const CXCursor& cur)
std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context,
const CXCursor& cur)
{
if (clang_isAttribute(clang_getCursorKind(cur)))
return nullptr;
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur,
"unexpected child cursor of enum");
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// <identifier> [<attribute>],
// or: <identifier> [<attribute>] = <expression>,
auto& name = stream.get().value();
auto attributes = detail::parse_attributes(stream);
std::unique_ptr<cpp_expression> value;
if (detail::skip_if(stream, "="))
{
if (clang_isAttribute(clang_getCursorKind(cur)))
return nullptr;
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !value, detail::parse_error_handler{},
cur, "unexpected child cursor of enum value");
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur,
"unexpected child cursor of enum");
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// <identifier> [<attribute>],
// or: <identifier> [<attribute>] = <expression>,
auto& name = stream.get().value();
auto attributes = detail::parse_attributes(stream);
std::unique_ptr<cpp_expression> value;
if (detail::skip_if(stream, "="))
{
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !value,
detail::parse_error_handler{}, cur,
"unexpected child cursor of enum value");
value = detail::parse_expression(context, child);
});
}
auto result = cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(value));
result->add_attribute(attributes);
return result;
value = detail::parse_expression(context, child);
});
}
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur,
type_safe::optional<cpp_entity_ref>& semantic_parent)
{
auto name = detail::get_cursor_name(cur);
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// enum [class/struct] [<attribute>] name [: type] {
detail::skip(stream, "enum");
auto scoped = detail::skip_if(stream, "class") || detail::skip_if(stream, "struct");
auto attributes = detail::parse_attributes(stream);
std::string scope;
while (!detail::skip_if(stream, name.c_str()))
if (!detail::append_scope(stream, scope))
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected tokens in enum name");
if (!scope.empty())
semantic_parent =
cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
// parse type
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
auto type_given = detail::skip_if(stream, ":");
auto result = cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
result.get().add_attribute(attributes);
return result;
}
auto result = cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(value));
result->add_attribute(attributes);
return result;
}
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur,
type_safe::optional<cpp_entity_ref>& semantic_parent)
{
auto name = detail::get_cursor_name(cur);
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// enum [class/struct] [<attribute>] name [: type] {
detail::skip(stream, "enum");
auto scoped = detail::skip_if(stream, "class") || detail::skip_if(stream, "struct");
auto attributes = detail::parse_attributes(stream);
std::string scope;
while (!detail::skip_if(stream, name.c_str()))
if (!detail::append_scope(stream, scope))
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected tokens in enum name");
if (!scope.empty())
semantic_parent = cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
// parse type
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
auto type_given = detail::skip_if(stream, ":");
auto result = cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
result.get().add_attribute(attributes);
return result;
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_enum(const detail::parse_context& context,
const CXCursor& cur)
{

View file

@ -4,11 +4,11 @@
#include <cppast/cpp_friend.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast;
@ -45,9 +45,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_friend(const detail::parse_context
// 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()));
type = cpp_user_defined_type::build(
cpp_type_ref(detail::get_entity_id(referenced),
namespace_str + "::" + name.c_str()));
}
else
{

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,8 @@
#include "parse_functions.hpp"
#include <cppast/cpp_language_linkage.hpp>
#include <clang-c/Index.h>
#include <cppast/cpp_language_linkage.hpp>
#include "libclang_visitor.hpp"

View file

@ -10,12 +10,12 @@
#include <clang-c/CXCompilationDatabase.h>
#include "cxtokenizer.hpp"
#include "libclang_visitor.hpp"
#include "raii_wrapper.hpp"
#include "parse_error.hpp"
#include "parse_functions.hpp"
#include "preprocessor.hpp"
#include "cxtokenizer.hpp"
#include "raii_wrapper.hpp"
using namespace cppast;
@ -81,22 +81,20 @@ bool libclang_compilation_database::has_config(const char* file_name) const
namespace
{
int parse_number(const char*& str)
int parse_number(const char*& str)
{
auto result = 0;
for (; *str && *str != '.'; ++str)
{
auto result = 0;
for (; *str && *str != '.'; ++str)
{
result *= 10;
result += int(*str - '0');
}
return result;
result *= 10;
result += int(*str - '0');
}
return result;
}
} // namespace
libclang_compile_config::libclang_compile_config()
: compile_config({}),
write_preprocessed_(false),
fast_preprocessing_(false),
: compile_config({}), write_preprocessed_(false), fast_preprocessing_(false),
remove_comments_in_macro_(false)
{
// set given clang binary
@ -117,39 +115,38 @@ libclang_compile_config::libclang_compile_config()
namespace
{
struct cxcompile_commands_deleter
struct cxcompile_commands_deleter
{
void operator()(CXCompileCommands cmds)
{
void operator()(CXCompileCommands cmds)
{
clang_CompileCommands_dispose(cmds);
}
};
using cxcompile_commands = detail::raii_wrapper<CXCompileCommands, cxcompile_commands_deleter>;
bool has_drive_prefix(const std::string& file)
{
return file.size() > 2 && file[1] == ':';
clang_CompileCommands_dispose(cmds);
}
};
bool is_absolute(const std::string& file)
{
return !file.empty()
&& (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\');
}
using cxcompile_commands = detail::raii_wrapper<CXCompileCommands, cxcompile_commands_deleter>;
std::string get_full_path(const detail::cxstring& dir, const std::string& file)
{
if (is_absolute(file))
// absolute file
return file;
else if (dir[dir.length() - 1] != '/' && dir[dir.length() - 1] != '\\')
// relative needing separator
return dir.std_str() + '/' + file;
else
// relative w/o separator
return dir.std_str() + file;
}
bool has_drive_prefix(const std::string& file)
{
return file.size() > 2 && file[1] == ':';
}
bool is_absolute(const std::string& file)
{
return !file.empty() && (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\');
}
std::string get_full_path(const detail::cxstring& dir, const std::string& file)
{
if (is_absolute(file))
// absolute file
return file;
else if (dir[dir.length() - 1] != '/' && dir[dir.length() - 1] != '\\')
// relative needing separator
return dir.std_str() + '/' + file;
else
// relative w/o separator
return dir.std_str() + file;
}
} // namespace
void detail::for_each_file(const libclang_compilation_database& database, void* user_data,
@ -170,70 +167,70 @@ void detail::for_each_file(const libclang_compilation_database& database, void*
namespace
{
bool is_flag(const detail::cxstring& str)
{
return str.length() > 1u && str[0] == '-';
}
bool is_flag(const detail::cxstring& str)
{
return str.length() > 1u && str[0] == '-';
}
const char* find_flag_arg_sep(const std::string& last_flag)
{
if (last_flag[1] == 'D')
// no separator, equal is part of the arg
return nullptr;
return std::strchr(last_flag.c_str(), '=');
}
const char* find_flag_arg_sep(const std::string& last_flag)
{
if (last_flag[1] == 'D')
// no separator, equal is part of the arg
return nullptr;
return std::strchr(last_flag.c_str(), '=');
}
template <typename Func>
void parse_flags(CXCompileCommand cmd, Func callback)
template <typename Func>
void parse_flags(CXCompileCommand cmd, Func callback)
{
auto no_args = clang_CompileCommand_getNumArgs(cmd);
std::string last_flag;
for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i)
{
auto no_args = clang_CompileCommand_getNumArgs(cmd);
std::string last_flag;
for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i)
detail::cxstring str(clang_CompileCommand_getArg(cmd, i));
if (is_flag(str))
{
detail::cxstring str(clang_CompileCommand_getArg(cmd, i));
if (is_flag(str))
if (!last_flag.empty())
{
if (!last_flag.empty())
// process last flag
std::string args;
if (auto ptr = find_flag_arg_sep(last_flag))
{
// process last flag
std::string args;
if (auto ptr = find_flag_arg_sep(last_flag))
{
auto pos = std::size_t(ptr - last_flag.c_str());
++ptr;
while (*ptr)
args += *ptr++;
last_flag.erase(pos);
}
else if (last_flag.size() > 2u)
{
// assume two character flag
args = last_flag.substr(2u);
last_flag.erase(2u);
}
callback(std::move(last_flag), std::move(args));
auto pos = std::size_t(ptr - last_flag.c_str());
++ptr;
while (*ptr)
args += *ptr++;
last_flag.erase(pos);
}
else if (last_flag.size() > 2u)
{
// assume two character flag
args = last_flag.substr(2u);
last_flag.erase(2u);
}
last_flag = str.std_str();
callback(std::move(last_flag), std::move(args));
}
else if (!last_flag.empty())
{
// we have flags + args
callback(std::move(last_flag), str.std_str());
last_flag.clear();
}
// else skip argument
last_flag = str.std_str();
}
else if (!last_flag.empty())
{
// we have flags + args
callback(std::move(last_flag), str.std_str());
last_flag.clear();
}
// else skip argument
}
}
} // namespace
libclang_compile_config::libclang_compile_config(const libclang_compilation_database& database,
const std::string& file)
: libclang_compile_config()
{
auto cxcommands =
clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str());
auto cxcommands
= clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str());
if (cxcommands == nullptr)
throw libclang_error(detail::format("no compile commands specified for file '", file, "'"));
cxcompile_commands commands(cxcommands);
@ -353,8 +350,8 @@ type_safe::optional<libclang_compile_config> cppast::find_config_for(
if (database.has_config(file_name))
return libclang_compile_config(database, std::move(file_name));
static const char* extensions[] = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx",
".cxx", ".hh", ".cc", ".H", ".C"};
static const char* extensions[]
= {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C"};
for (auto ext : extensions)
{
auto name = file_name + ext;
@ -370,125 +367,122 @@ struct libclang_parser::impl
detail::cxindex index;
impl() : index(clang_createIndex(0, 0)) // no diagnostic, other one is irrelevant
{
}
{}
};
libclang_parser::libclang_parser() : libclang_parser(default_logger()) {}
libclang_parser::libclang_parser(type_safe::object_ref<const diagnostic_logger> logger)
: parser(logger), pimpl_(new impl)
{
}
{}
libclang_parser::~libclang_parser() noexcept {}
namespace
{
std::vector<const char*> get_arguments(const libclang_compile_config& config)
std::vector<const char*> get_arguments(const libclang_compile_config& config)
{
std::vector<const char*> args
= {"-x", "c++", "-I."}; // force C++ and enable current directory for include search
for (auto& flag : detail::libclang_compile_config_access::flags(config))
args.push_back(flag.c_str());
return args;
}
type_safe::optional<severity> get_severity(const CXDiagnostic& diag)
{
switch (clang_getDiagnosticSeverity(diag))
{
std::vector<const char*> args =
{"-x", "c++", "-I."}; // force C++ and enable current directory for include search
for (auto& flag : detail::libclang_compile_config_access::flags(config))
args.push_back(flag.c_str());
return args;
}
type_safe::optional<severity> get_severity(const CXDiagnostic& diag)
{
switch (clang_getDiagnosticSeverity(diag))
{
case CXDiagnostic_Ignored:
case CXDiagnostic_Note:
case CXDiagnostic_Warning:
// ignore those diagnostics
return type_safe::nullopt;
case CXDiagnostic_Error:
return severity::error;
case CXDiagnostic_Fatal:
return severity::critical;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
case CXDiagnostic_Ignored:
case CXDiagnostic_Note:
case CXDiagnostic_Warning:
// ignore those diagnostics
return type_safe::nullopt;
case CXDiagnostic_Error:
return severity::error;
case CXDiagnostic_Fatal:
return severity::critical;
}
void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu)
{
auto no = clang_getNumDiagnostics(tu);
for (auto i = 0u; i != no; ++i)
{
auto diag = clang_getDiagnostic(tu, i);
auto sev = get_severity(diag);
if (sev)
{
auto diag_loc = clang_getDiagnosticLocation(diag);
CXString diag_file;
unsigned line;
clang_getPresumedLocation(diag_loc, &diag_file, &line, nullptr);
DEBUG_UNREACHABLE(detail::assert_handler{});
return type_safe::nullopt;
}
auto loc = source_location::make_file(detail::cxstring(diag_file).c_str(), line);
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
if (text != "too many errors emitted, stopping now")
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()});
}
void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu)
{
auto no = clang_getNumDiagnostics(tu);
for (auto i = 0u; i != no; ++i)
{
auto diag = clang_getDiagnostic(tu, i);
auto sev = get_severity(diag);
if (sev)
{
auto diag_loc = clang_getDiagnosticLocation(diag);
CXString diag_file;
unsigned line;
clang_getPresumedLocation(diag_loc, &diag_file, &line, nullptr);
auto loc = source_location::make_file(detail::cxstring(diag_file).c_str(), line);
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
if (text != "too many errors emitted, stopping now")
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()});
}
}
}
detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger,
const detail::cxindex& idx,
const libclang_compile_config& config, const char* path,
const std::string& source)
detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger, const detail::cxindex& idx,
const libclang_compile_config& config, const char* path,
const std::string& source)
{
CXUnsavedFile file;
file.Filename = path;
file.Contents = source.c_str();
file.Length = source.length();
auto args = get_arguments(config);
CXTranslationUnit tu;
auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing
| CXTranslationUnit_DetailedPreprocessingRecord;
auto error
= clang_parseTranslationUnit2(idx.get(), path, // index and path
args.data(),
static_cast<int>(args.size()), // arguments (ptr + size)
&file, 1, // unsaved files (ptr + size)
unsigned(flags), &tu);
if (error != CXError_Success)
{
CXUnsavedFile file;
file.Filename = path;
file.Contents = source.c_str();
file.Length = source.length();
auto args = get_arguments(config);
CXTranslationUnit tu;
auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing
| CXTranslationUnit_DetailedPreprocessingRecord;
auto error =
clang_parseTranslationUnit2(idx.get(), path, // index and path
args.data(),
static_cast<int>(args.size()), // arguments (ptr + size)
&file, 1, // unsaved files (ptr + size)
unsigned(flags), &tu);
if (error != CXError_Success)
switch (error)
{
switch (error)
{
case CXError_Success:
DEBUG_UNREACHABLE(detail::assert_handler{});
break;
case CXError_Success:
DEBUG_UNREACHABLE(detail::assert_handler{});
break;
case CXError_Failure:
throw libclang_error("clang_parseTranslationUnit: generic error");
case CXError_Crashed:
throw libclang_error("clang_parseTranslationUnit: libclang crashed :(");
case CXError_InvalidArguments:
throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message");
case CXError_ASTReadError:
throw libclang_error("clang_parseTranslationUnit: AST deserialization error");
}
case CXError_Failure:
throw libclang_error("clang_parseTranslationUnit: generic error");
case CXError_Crashed:
throw libclang_error("clang_parseTranslationUnit: libclang crashed :(");
case CXError_InvalidArguments:
throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message");
case CXError_ASTReadError:
throw libclang_error("clang_parseTranslationUnit: AST deserialization error");
}
print_diagnostics(logger, tu);
return detail::cxtranslation_unit(tu);
}
print_diagnostics(logger, tu);
unsigned get_line_no(const CXCursor& cursor)
{
auto loc = clang_getCursorLocation(cursor);
return detail::cxtranslation_unit(tu);
}
unsigned line;
clang_getPresumedLocation(loc, nullptr, &line, nullptr);
return line;
}
unsigned get_line_no(const CXCursor& cursor)
{
auto loc = clang_getCursorLocation(cursor);
unsigned line;
clang_getPresumedLocation(loc, nullptr, &line, nullptr);
return line;
}
} // namespace
std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx, std::string path,
@ -530,8 +524,8 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
&& get_line_no(cur) >= include_iter->line,
detail::assert_handler{});
auto full_path = include_iter->full_path.empty() ? include_iter->file_name :
include_iter->full_path;
auto full_path = include_iter->full_path.empty() ? include_iter->file_name
: include_iter->full_path;
// if we got an absolute file path for the current file,
// also use an absolute file path for the id
@ -543,10 +537,10 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
else
id = cpp_entity_id(include_iter->file_name.c_str());
auto include =
cpp_include_directive::build(cpp_file_ref(id,
std::move(include_iter->file_name)),
include_iter->kind, std::move(full_path));
auto include
= cpp_include_directive::build(cpp_file_ref(id,
std::move(include_iter->file_name)),
include_iter->kind, std::move(full_path));
context.comments.match(*include, include_iter->line,
false); // must not skip comments,
// includes are not reported in order

View file

@ -11,50 +11,50 @@
namespace cppast
{
namespace detail
namespace detail
{
// visits direct children of an entity
template <typename Func>
void visit_children(CXCursor parent, Func f, bool recurse = false)
{
// visits direct children of an entity
template <typename Func>
void visit_children(CXCursor parent, Func f, bool recurse = false)
{
auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
auto& actual_cb = *static_cast<Func*>(data);
actual_cb(cur);
return CXChildVisit_Continue;
};
auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
auto& actual_cb = *static_cast<Func*>(data);
actual_cb(cur);
return CXChildVisit_Recurse;
};
auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
auto& actual_cb = *static_cast<Func*>(data);
actual_cb(cur);
return CXChildVisit_Continue;
};
auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
auto& actual_cb = *static_cast<Func*>(data);
actual_cb(cur);
return CXChildVisit_Recurse;
};
if (recurse)
clang_visitChildren(parent, recurse_lambda, &f);
else
clang_visitChildren(parent, continue_lambda, &f);
}
// visits a translation unit
// notes: only visits if directly defined in file, not included
template <typename Func>
void visit_tu(const cxtranslation_unit& tu, const char* path, Func f)
{
auto in_tu = [&](const CXCursor& cur) {
auto location = clang_getCursorLocation(cur);
CXString cx_file_name;
clang_getPresumedLocation(location, &cx_file_name, nullptr, nullptr);
cxstring file_name(cx_file_name);
return file_name == path;
};
visit_children(clang_getTranslationUnitCursor(tu.get()), [&](const CXCursor& cur) {
if (in_tu(cur))
f(cur);
});
}
if (recurse)
clang_visitChildren(parent, recurse_lambda, &f);
else
clang_visitChildren(parent, continue_lambda, &f);
}
} // namespace cppast::detail
// visits a translation unit
// notes: only visits if directly defined in file, not included
template <typename Func>
void visit_tu(const cxtranslation_unit& tu, const char* path, Func f)
{
auto in_tu = [&](const CXCursor& cur) {
auto location = clang_getCursorLocation(cur);
CXString cx_file_name;
clang_getPresumedLocation(location, &cx_file_name, nullptr, nullptr);
cxstring file_name(cx_file_name);
return file_name == path;
};
visit_children(clang_getTranslationUnitCursor(tu.get()), [&](const CXCursor& cur) {
if (in_tu(cur))
f(cur);
});
}
} // namespace detail
} // namespace cppast
#endif // CPPAST_LIBCLANG_VISITOR_HPP_INCLUDED

View file

@ -13,49 +13,48 @@ using namespace cppast;
namespace
{
cpp_namespace::builder make_ns_builder(const detail::parse_context& context,
const CXCursor& cur)
cpp_namespace::builder make_ns_builder(const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [inline] namespace|:: [<attribute>] <identifier> [{]
auto is_inline = false;
if (skip_if(stream, "inline"))
is_inline = true;
// C++17 nested namespace declarations get one cursor per nested name.
// The first cursor starts with the `namespace` keyword, and the
// following start with the `::` separator. Either way, it is skipped.
auto is_nested = false;
if (!detail::skip_if(stream, "namespace"))
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [inline] namespace|:: [<attribute>] <identifier> [{]
auto is_inline = false;
if (skip_if(stream, "inline"))
is_inline = true;
// C++17 nested namespace declarations get one cursor per nested name.
// The first cursor starts with the `namespace` keyword, and the
// following start with the `::` separator. Either way, it is skipped.
auto is_nested = false;
if (!detail::skip_if(stream, "namespace"))
{
is_nested = true;
skip(stream, "::");
}
auto attributes = parse_attributes(stream);
// <identifier> {
// or when anonymous: {
if (detail::skip_if(stream, "{"))
return cpp_namespace::builder("", is_inline, false);
auto& name = stream.get().value();
auto other_attributes = parse_attributes(stream);
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
// If the next token is not `::`, there are no more nested namespace
// names, and we expect to see an opening brace.
if (!detail::skip_if(stream, "::"))
skip(stream, "{");
auto result = cpp_namespace::builder(name.c_str(), is_inline, is_nested);
result.get().add_attribute(attributes);
return result;
is_nested = true;
skip(stream, "::");
}
auto attributes = parse_attributes(stream);
// <identifier> {
// or when anonymous: {
if (detail::skip_if(stream, "{"))
return cpp_namespace::builder("", is_inline, false);
auto& name = stream.get().value();
auto other_attributes = parse_attributes(stream);
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
// If the next token is not `::`, there are no more nested namespace
// names, and we expect to see an opening brace.
if (!detail::skip_if(stream, "::"))
skip(stream, "{");
auto result = cpp_namespace::builder(name.c_str(), is_inline, is_nested);
result.get().add_attribute(attributes);
return result;
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_context& context,
cpp_entity& parent, const CXCursor& cur)
@ -83,27 +82,27 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_cont
namespace
{
cpp_entity_id parse_ns_target_cursor(const CXCursor& cur)
{
cpp_entity_id result("");
detail::visit_children(cur,
[&](const CXCursor& child) {
auto referenced = clang_getCursorReferenced(child);
auto kind = clang_getCursorKind(referenced);
if (kind == CXCursor_Namespace)
result = detail::get_entity_id(referenced);
else if (kind == CXCursor_NamespaceAlias)
// get target of namespace alias instead
result = parse_ns_target_cursor(referenced);
else
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for namespace "
"alias/using directive");
},
true);
return result;
}
cpp_entity_id parse_ns_target_cursor(const CXCursor& cur)
{
cpp_entity_id result("");
detail::visit_children(cur,
[&](const CXCursor& child) {
auto referenced = clang_getCursorReferenced(child);
auto kind = clang_getCursorKind(referenced);
if (kind == CXCursor_Namespace)
result = detail::get_entity_id(referenced);
else if (kind == CXCursor_NamespaceAlias)
// get target of namespace alias instead
result = parse_ns_target_cursor(referenced);
else
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for namespace "
"alias/using directive");
},
true);
return result;
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace_alias(const detail::parse_context& context,
const CXCursor& cur)
@ -155,54 +154,53 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
namespace
{
cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name)
{
type_safe::deferred_construction<cpp_entity_ref> result;
detail::visit_children(cur,
[&](const CXCursor& child) {
if (result)
return;
cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name)
{
type_safe::deferred_construction<cpp_entity_ref> result;
detail::visit_children(cur,
[&](const CXCursor& child) {
if (result)
return;
switch (clang_getCursorKind(child))
{
case CXCursor_TypeRef:
case CXCursor_TemplateRef:
case CXCursor_MemberRef:
case CXCursor_VariableRef:
case CXCursor_DeclRefExpr:
{
auto referenced = clang_getCursorReferenced(child);
result = cpp_entity_ref(detail::get_entity_id(referenced),
std::move(name));
break;
}
switch (clang_getCursorKind(child))
{
case CXCursor_TypeRef:
case CXCursor_TemplateRef:
case CXCursor_MemberRef:
case CXCursor_VariableRef:
case CXCursor_DeclRefExpr:
{
auto referenced = clang_getCursorReferenced(child);
result = cpp_entity_ref(detail::get_entity_id(referenced),
std::move(name));
break;
}
case CXCursor_OverloadedDeclRef:
{
auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur,
"no target for using declaration");
std::vector<cpp_entity_id> ids;
for (auto i = 0u; i != size; ++i)
ids.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, i)));
result = cpp_entity_ref(std::move(ids), std::move(name));
break;
}
case CXCursor_OverloadedDeclRef:
{
auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur,
"no target for using declaration");
std::vector<cpp_entity_id> ids;
for (auto i = 0u; i != size; ++i)
ids.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, i)));
result = cpp_entity_ref(std::move(ids), std::move(name));
break;
}
case CXCursor_NamespaceRef:
break; // wait for children
case CXCursor_NamespaceRef:
break; // wait for children
default:
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for using declaration");
}
},
true);
return result.value();
}
default:
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for using declaration");
}
},
true);
return result.value();
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_using_declaration(
const detail::parse_context& context, const CXCursor& cur)

View file

@ -7,89 +7,86 @@
#include <stdexcept>
#include <debug_assert.hpp>
#include <cppast/diagnostic.hpp>
#include <debug_assert.hpp>
#include "debug_helper.hpp"
namespace cppast
{
namespace detail
namespace detail
{
inline source_location make_location(const CXCursor& cur)
{
inline source_location make_location(const CXCursor& cur)
{
auto loc = clang_getCursorLocation(cur);
auto loc = clang_getCursorLocation(cur);
CXString file;
unsigned line;
clang_getPresumedLocation(loc, &file, &line, nullptr);
CXString file;
unsigned line;
clang_getPresumedLocation(loc, &file, &line, nullptr);
return source_location::make_file(cxstring(file).c_str(), line);
}
inline source_location make_location(const CXFile& file, const CXCursor& cur)
{
return source_location::make_entity(get_display_name(cur).c_str(),
cxstring(clang_getFileName(file)).c_str());
}
inline source_location make_location(const CXType& type)
{
return source_location::make_entity(cxstring(clang_getTypeSpelling(type)).c_str());
}
// thrown on a parsing error
// not meant to escape to the user
class parse_error : public std::logic_error
{
public:
parse_error(source_location loc, std::string message)
: std::logic_error(std::move(message)), location_(std::move(loc))
{
}
parse_error(const CXCursor& cur, std::string message)
: parse_error(make_location(cur), std::move(message))
{
}
parse_error(const CXType& type, std::string message)
: parse_error(make_location(type), std::move(message))
{
}
diagnostic get_diagnostic(const CXFile& file)
{
return get_diagnostic(cxstring(clang_getFileName(file)).c_str());
}
diagnostic get_diagnostic(std::string file)
{
location_.file = std::move(file);
return diagnostic{what(), location_, severity::error};
}
private:
source_location location_;
};
// DEBUG_ASSERT handler for parse errors
// throws a parse_error exception
struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception
{
static void handle(const debug_assert::source_location&, const char*,
const CXCursor& cur, std::string message)
{
throw parse_error(cur, std::move(message));
}
static void handle(const debug_assert::source_location&, const char*,
const CXType& type, std::string message)
{
throw parse_error(type, std::move(message));
}
};
return source_location::make_file(cxstring(file).c_str(), line);
}
} // namespace cppast::detail
inline source_location make_location(const CXFile& file, const CXCursor& cur)
{
return source_location::make_entity(get_display_name(cur).c_str(),
cxstring(clang_getFileName(file)).c_str());
}
inline source_location make_location(const CXType& type)
{
return source_location::make_entity(cxstring(clang_getTypeSpelling(type)).c_str());
}
// thrown on a parsing error
// not meant to escape to the user
class parse_error : public std::logic_error
{
public:
parse_error(source_location loc, std::string message)
: std::logic_error(std::move(message)), location_(std::move(loc))
{}
parse_error(const CXCursor& cur, std::string message)
: parse_error(make_location(cur), std::move(message))
{}
parse_error(const CXType& type, std::string message)
: parse_error(make_location(type), std::move(message))
{}
diagnostic get_diagnostic(const CXFile& file)
{
return get_diagnostic(cxstring(clang_getFileName(file)).c_str());
}
diagnostic get_diagnostic(std::string file)
{
location_.file = std::move(file);
return diagnostic{what(), location_, severity::error};
}
private:
source_location location_;
};
// DEBUG_ASSERT handler for parse errors
// throws a parse_error exception
struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception
{
static void handle(const debug_assert::source_location&, const char*, const CXCursor& cur,
std::string message)
{
throw parse_error(cur, std::move(message));
}
static void handle(const debug_assert::source_location&, const char*, const CXType& type,
std::string message)
{
throw parse_error(type, std::move(message));
}
};
} // namespace detail
} // namespace cppast
#endif // CPPAST_PARSE_ERROR_HPP_INCLUDED

View file

@ -4,8 +4,8 @@
#include "parse_functions.hpp"
#include <cppast/cpp_storage_class_specifiers.hpp>
#include <cppast/cpp_static_assert.hpp>
#include <cppast/cpp_storage_class_specifiers.hpp>
#include "libclang_visitor.hpp"
@ -100,16 +100,16 @@ void detail::comment_context::match(cpp_entity& e, unsigned line, bool skip_comm
namespace
{
bool is_friend(const CXCursor& parent_cur)
{
bool is_friend(const CXCursor& parent_cur)
{
#if CPPAST_CINDEX_HAS_FRIEND
return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl;
return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl;
#else
(void)parent_cur;
return false;
(void)parent_cur;
return false;
#endif
}
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
cpp_entity* parent, const CXCursor& cur,
@ -165,25 +165,25 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
return parse_cpp_member_variable(context, cur);
case CXCursor_FunctionDecl:
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
if (auto tfunc
= try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_function(context, cur, is_friend(parent_cur));
case CXCursor_CXXMethod:
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
if (auto tfunc
= try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
else if (auto func = try_parse_static_cpp_function(context, cur))
return func;
return parse_cpp_member_function(context, cur, is_friend(parent_cur));
case CXCursor_ConversionFunction:
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
if (auto tfunc
= try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_conversion_op(context, cur, is_friend(parent_cur));
case CXCursor_Constructor:
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
if (auto tfunc
= try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_constructor(context, cur, is_friend(parent_cur));
case CXCursor_Destructor:

View file

@ -8,166 +8,161 @@
#include <cppast/cpp_entity.hpp>
#include <cppast/parser.hpp>
#include "raii_wrapper.hpp"
#include "cxtokenizer.hpp" // for convenience
#include "parse_error.hpp" // for convenience
#include "preprocessor.hpp"
#include "raii_wrapper.hpp"
#if CINDEX_VERSION_MINOR >= 36
#define CPPAST_CINDEX_HAS_FRIEND 1
# define CPPAST_CINDEX_HAS_FRIEND 1
#else
#define CPPAST_CINDEX_HAS_FRIEND 0
# define CPPAST_CINDEX_HAS_FRIEND 0
#endif
namespace cppast
{
class cpp_expression;
class cpp_type;
enum cpp_storage_class_specifiers : int;
class cpp_expression;
class cpp_type;
enum cpp_storage_class_specifiers : int;
namespace detail
namespace detail
{
cpp_entity_id get_entity_id(const CXCursor& cur);
// only use this if the name is just a single token
// never where it is a reference to something (like base class name)
// as then you won't get it "as-is"
cxstring get_cursor_name(const CXCursor& cur);
// note: does not handle thread_local
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur);
class comment_context
{
cpp_entity_id get_entity_id(const CXCursor& cur);
public:
explicit comment_context(std::vector<pp_doc_comment>& comments)
: cur_(comments.data()), end_(comments.data() + comments.size())
{}
// only use this if the name is just a single token
// never where it is a reference to something (like base class name)
// as then you won't get it "as-is"
cxstring get_cursor_name(const CXCursor& cur);
// must be called for entities that want an associated comment
// must be called *BEFORE* the children are added
void match(cpp_entity& e, const CXCursor& cur) const;
void match(cpp_entity& e, unsigned line, bool skip_comments = true) const;
// note: does not handle thread_local
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur);
private:
mutable pp_doc_comment* cur_;
pp_doc_comment* end_;
};
class comment_context
{
public:
explicit comment_context(std::vector<pp_doc_comment>& comments)
: cur_(comments.data()), end_(comments.data() + comments.size())
{
}
struct parse_context
{
CXTranslationUnit tu;
CXFile file;
type_safe::object_ref<const diagnostic_logger> logger;
type_safe::object_ref<const cpp_entity_index> idx;
comment_context comments;
mutable bool error;
};
// must be called for entities that want an associated comment
// must be called *BEFORE* the children are added
void match(cpp_entity& e, const CXCursor& cur) const;
void match(cpp_entity& e, unsigned line, bool skip_comments = true) const;
// parse default value of variable, function parameter...
std::unique_ptr<cpp_expression> parse_default_value(cpp_attribute_list& attributes,
const parse_context& context,
const CXCursor& cur, const char* name);
private:
mutable pp_doc_comment* cur_;
pp_doc_comment* end_;
};
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
const CXType& type);
struct parse_context
{
CXTranslationUnit tu;
CXFile file;
type_safe::object_ref<const diagnostic_logger> logger;
type_safe::object_ref<const cpp_entity_index> idx;
comment_context comments;
mutable bool error;
};
// parse the type starting at the current token stream
// and ends at the given iterator
// this is required for situations where there is no type exposed,
// like default type of a template type parameter
std::unique_ptr<cpp_type> parse_raw_type(const parse_context& context, cxtoken_stream& stream,
cxtoken_iterator end);
// parse default value of variable, function parameter...
std::unique_ptr<cpp_expression> parse_default_value(cpp_attribute_list& attributes,
const parse_context& context,
const CXCursor& cur, const char* name);
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
const CXType& type);
// parse the type starting at the current token stream
// and ends at the given iterator
// this is required for situations where there is no type exposed,
// like default type of a template type parameter
std::unique_ptr<cpp_type> parse_raw_type(const parse_context& context,
cxtoken_stream& stream, cxtoken_iterator end);
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse the expression starting at the current token in the stream
// and ends at the given iterator
// this is required for situations where there is no expression cursor exposed,
// like member initializers
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
cxtoken_stream& stream,
cxtoken_iterator end,
std::unique_ptr<cpp_type> type);
// parse_entity() dispatches on the cursor type
// it calls one of the other parse functions defined elsewhere
// try_parse_XXX are not exposed/differently exposed entities
// they are called on corresponding cursor and see whether they match
// unexposed
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
const CXCursor& cur);
// CXXMethod
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
const CXCursor& cur);
// on all function cursors except on destructor
std::unique_ptr<cpp_entity> try_parse_cpp_function_template_specialization(
const parse_context& context, const CXCursor& cur, bool is_friend);
// on class cursors
std::unique_ptr<cpp_entity> try_parse_full_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
cpp_entity& parent, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_declaration(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur,
const CXCursor& template_cur);
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& parent_cur);
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
// also parses bitfields
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_conversion_op(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_constructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_friend(const parse_context& context,
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse the expression starting at the current token in the stream
// and ends at the given iterator
// this is required for situations where there is no expression cursor exposed,
// like member initializers
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
cxtoken_stream& stream,
cxtoken_iterator end,
std::unique_ptr<cpp_type> type);
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,
const CXCursor& cur,
bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_class_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
// parse_entity() dispatches on the cursor type
// it calls one of the other parse functions defined elsewhere
// try_parse_XXX are not exposed/differently exposed entities
// they are called on corresponding cursor and see whether they match
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context,
// unexposed
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
const CXCursor& cur);
// CXXMethod
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
const CXCursor& cur);
// on all function cursors except on destructor
std::unique_ptr<cpp_entity> try_parse_cpp_function_template_specialization(
const parse_context& context, const CXCursor& cur, bool is_friend);
// on class cursors
std::unique_ptr<cpp_entity> try_parse_full_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
cpp_entity& parent, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_declaration(const parse_context& context,
const CXCursor& cur);
// parent: used for nested namespace, doesn't matter otherwise
// parent_cur: used when parsing templates or friends
std::unique_ptr<cpp_entity> parse_entity(
const parse_context& context, cpp_entity* parent, const CXCursor& cur,
const CXCursor& parent_cur = clang_getNullCursor());
}
} // namespace cppast::detail
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur,
const CXCursor& template_cur);
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& parent_cur);
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
// also parses bitfields
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_conversion_op(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_constructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
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,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_class_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context,
const CXCursor& cur);
// parent: used for nested namespace, doesn't matter otherwise
// parent_cur: used when parsing templates or friends
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, cpp_entity* parent,
const CXCursor& cur,
const CXCursor& parent_cur = clang_getNullCursor());
} // namespace detail
} // namespace cppast
#endif // CPPAST_PARSE_FUNCTIONS_HPP_INCLUDED

File diff suppressed because it is too large Load diff

View file

@ -10,46 +10,46 @@
namespace cppast
{
namespace detail
namespace detail
{
struct pp_macro
{
struct pp_macro
std::unique_ptr<cpp_macro_definition> macro;
unsigned line;
};
struct pp_include
{
std::string file_name, full_path;
cpp_include_kind kind;
unsigned line;
};
struct pp_doc_comment
{
std::string comment;
unsigned line;
enum
{
std::unique_ptr<cpp_macro_definition> macro;
unsigned line;
};
c,
cpp,
end_of_line,
} kind;
struct pp_include
{
std::string file_name, full_path;
cpp_include_kind kind;
unsigned line;
};
bool matches(const cpp_entity& e, unsigned line);
};
struct pp_doc_comment
{
std::string comment;
unsigned line;
enum
{
c,
cpp,
end_of_line,
} kind;
struct preprocessor_output
{
std::string source;
std::vector<pp_include> includes;
std::vector<pp_macro> macros;
std::vector<pp_doc_comment> comments;
};
bool matches(const cpp_entity& e, unsigned line);
};
struct preprocessor_output
{
std::string source;
std::vector<pp_include> includes;
std::vector<pp_macro> macros;
std::vector<pp_doc_comment> comments;
};
preprocessor_output preprocess(const libclang_compile_config& config, const char* path,
const diagnostic_logger& logger);
}
} // namespace cppast::detail
preprocessor_output preprocess(const libclang_compile_config& config, const char* path,
const diagnostic_logger& logger);
} // namespace detail
} // namespace cppast
#endif // CPPAST_PREPROCESSOR_HPP_INCLUDED

View file

@ -16,170 +16,169 @@
namespace cppast
{
namespace detail
namespace detail
{
template <typename T, class Deleter>
class raii_wrapper : Deleter
{
template <typename T, class Deleter>
class raii_wrapper : Deleter
static_assert(std::is_pointer<T>::value, "");
public:
raii_wrapper() noexcept : obj_(nullptr) {}
explicit raii_wrapper(T obj) noexcept : obj_(obj)
{
static_assert(std::is_pointer<T>::value, "");
DEBUG_ASSERT(obj_, detail::assert_handler{});
}
public:
raii_wrapper() noexcept : obj_(nullptr) {}
raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_)
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
other.obj_ = nullptr;
}
explicit raii_wrapper(T obj) noexcept : obj_(obj)
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
}
~raii_wrapper() noexcept
{
if (obj_)
static_cast<Deleter&> (*this)(obj_);
}
raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_)
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
other.obj_ = nullptr;
}
raii_wrapper& operator=(raii_wrapper&& other) noexcept
{
raii_wrapper tmp(std::move(other));
swap(*this, tmp);
return *this;
}
~raii_wrapper() noexcept
{
if (obj_)
static_cast<Deleter&> (*this)(obj_);
}
friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept
{
std::swap(a.obj_, b.obj_);
}
raii_wrapper& operator=(raii_wrapper&& other) noexcept
{
raii_wrapper tmp(std::move(other));
swap(*this, tmp);
return *this;
}
T get() const noexcept
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
return obj_;
}
friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept
{
std::swap(a.obj_, b.obj_);
}
private:
T obj_;
};
T get() const noexcept
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
return obj_;
}
struct cxindex_deleter
{
void operator()(CXIndex idx) noexcept
{
clang_disposeIndex(idx);
}
};
private:
T obj_;
using cxindex = raii_wrapper<CXIndex, cxindex_deleter>;
struct cxtranslation_unit_deleter
{
void operator()(CXTranslationUnit unit) noexcept
{
clang_disposeTranslationUnit(unit);
}
};
using cxtranslation_unit = raii_wrapper<CXTranslationUnit, cxtranslation_unit_deleter>;
class cxstring
{
public:
explicit cxstring(CXString str) noexcept : str_(string(str)) {}
cxstring(cxstring&& other) noexcept : str_(other.str_)
{
other.str_.reset();
}
cxstring& operator=(cxstring&& other) noexcept
{
if (str_)
clang_disposeString(str_.value().str);
str_ = other.str_;
other.str_.reset();
return *this;
}
~cxstring() noexcept
{
if (str_)
clang_disposeString(str_.value().str);
}
const char* c_str() const noexcept
{
return str_ ? str_.value().c_str : "";
}
std::string std_str() const noexcept
{
return c_str();
}
char operator[](std::size_t i) const noexcept
{
return c_str()[i];
}
std::size_t length() const noexcept
{
return str_ ? str_.value().length : 0u;
}
bool empty() const noexcept
{
return length() == 0u;
}
private:
struct string
{
CXString str;
const char* c_str;
std::size_t length;
explicit string(CXString str)
: str(std::move(str)), c_str(clang_getCString(str)), length(std::strlen(c_str))
{}
};
type_safe::optional<string> str_;
};
struct cxindex_deleter
{
void operator()(CXIndex idx) noexcept
{
clang_disposeIndex(idx);
}
};
using cxindex = raii_wrapper<CXIndex, cxindex_deleter>;
struct cxtranslation_unit_deleter
{
void operator()(CXTranslationUnit unit) noexcept
{
clang_disposeTranslationUnit(unit);
}
};
using cxtranslation_unit = raii_wrapper<CXTranslationUnit, cxtranslation_unit_deleter>;
class cxstring
{
public:
explicit cxstring(CXString str) noexcept : str_(string(str)) {}
cxstring(cxstring&& other) noexcept : str_(other.str_)
{
other.str_.reset();
}
cxstring& operator=(cxstring&& other) noexcept
{
if (str_)
clang_disposeString(str_.value().str);
str_ = other.str_;
other.str_.reset();
return *this;
}
~cxstring() noexcept
{
if (str_)
clang_disposeString(str_.value().str);
}
const char* c_str() const noexcept
{
return str_ ? str_.value().c_str : "";
}
std::string std_str() const noexcept
{
return c_str();
}
char operator[](std::size_t i) const noexcept
{
return c_str()[i];
}
std::size_t length() const noexcept
{
return str_ ? str_.value().length : 0u;
}
bool empty() const noexcept
{
return length() == 0u;
}
private:
struct string
{
CXString str;
const char* c_str;
std::size_t length;
explicit string(CXString str)
: str(std::move(str)), c_str(clang_getCString(str)), length(std::strlen(c_str))
{
}
};
type_safe::optional<string> str_;
};
inline bool operator==(const cxstring& a, const cxstring& b) noexcept
{
return std::strcmp(a.c_str(), b.c_str()) == 0;
}
inline bool operator==(const cxstring& a, const char* str) noexcept
{
return std::strcmp(a.c_str(), str) == 0;
}
inline bool operator==(const char* str, const cxstring& b) noexcept
{
return std::strcmp(str, b.c_str()) == 0;
}
inline bool operator!=(const cxstring& a, const cxstring& b) noexcept
{
return !(a == b);
}
inline bool operator!=(const cxstring& a, const char* str) noexcept
{
return !(a == str);
}
inline bool operator!=(const char* str, const cxstring& b) noexcept
{
return !(str == b);
}
inline bool operator==(const cxstring& a, const cxstring& b) noexcept
{
return std::strcmp(a.c_str(), b.c_str()) == 0;
}
} // namespace cppast::detail
inline bool operator==(const cxstring& a, const char* str) noexcept
{
return std::strcmp(a.c_str(), str) == 0;
}
inline bool operator==(const char* str, const cxstring& b) noexcept
{
return std::strcmp(str, b.c_str()) == 0;
}
inline bool operator!=(const cxstring& a, const cxstring& b) noexcept
{
return !(a == b);
}
inline bool operator!=(const cxstring& a, const char* str) noexcept
{
return !(a == str);
}
inline bool operator!=(const char* str, const cxstring& b) noexcept
{
return !(str == b);
}
} // namespace detail
} // namespace cppast
#endif // CPPAST_RAII_WRAPPER_HPP_INCLUDED

View file

@ -15,214 +15,209 @@ using namespace cppast;
namespace
{
template <typename TemplateT, typename EntityT, typename Predicate>
type_safe::optional<typename TemplateT::builder> get_builder(
const detail::parse_context& context, const CXCursor& cur, Predicate p)
{
// we need the actual entity first, then the parameters
// so two visit calls are required
template <typename TemplateT, typename EntityT, typename Predicate>
type_safe::optional<typename TemplateT::builder> get_builder(const detail::parse_context& context,
const CXCursor& cur, Predicate p)
{
// we need the actual entity first, then the parameters
// so two visit calls are required
auto result = clang_getNullCursor();
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
return;
DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"unexpected child of template");
result = child;
});
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"missing child of template");
auto result = clang_getNullCursor();
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
return;
DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"unexpected child of template");
result = child;
});
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"missing child of template");
auto entity = detail::parse_entity(context, nullptr, result, cur);
if (!entity)
return type_safe::nullopt;
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur,
"wrong child of template");
return typename TemplateT::builder(
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
}
auto entity = detail::parse_entity(context, nullptr, result, cur);
if (!entity)
return type_safe::nullopt;
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur, "wrong child of template");
return typename TemplateT::builder(
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
}
std::unique_ptr<cpp_template_parameter> parse_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
detail::assert_handler{});
std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: typename/class [...] name [= ...]
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
// syntax: typename/class [...] name [= ...]
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto variadic = false;
if (detail::skip_if(stream, "..."))
variadic = true;
auto variadic = false;
if (detail::skip_if(stream, "..."))
variadic = true;
if (stream.peek() != "=")
detail::skip(stream, name.c_str());
std::unique_ptr<cpp_type> def;
if (detail::skip_if(stream, "="))
// default type
def = detail::parse_raw_type(context, stream, stream.end());
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(), keyword, variadic, std::move(def));
}
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = clang_getCursorType(cur);
cpp_attribute_list attributes;
auto def = detail::parse_default_value(attributes, context, cur, name.c_str());
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// see if it is variadic
// syntax a): some-tokens ... name some-tokens
// syntax b): some-tokens (some-tokens ... name) some-tokens-or-...
// name might be empty, so can't loop until it occurs
// some-tokens will not contain ... or parenthesis, however
auto is_variadic = false;
for (; !stream.done(); stream.bump())
{
if (stream.peek() == "...")
{
is_variadic = true;
break;
}
else if (stream.peek() == ")")
break;
}
auto result =
cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(),
detail::parse_type(context, cur, type),
is_variadic, std::move(def));
result->add_attribute(attributes);
return std::move(result);
}
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter,
detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: template <…> class/typename [...] name [= …]
detail::skip(stream, "template");
detail::skip_brackets(stream);
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto is_variadic = detail::skip_if(stream, "...");
if (stream.peek() != "=")
detail::skip(stream, name.c_str());
// now we can create the builder
cpp_template_template_parameter::builder builder(name.c_str(), is_variadic);
builder.keyword(keyword);
std::unique_ptr<cpp_type> def;
if (detail::skip_if(stream, "="))
// default type
def = detail::parse_raw_type(context, stream, stream.end());
// look for parameters and default
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
else if (kind == CXCursor_TemplateRef)
{
auto target = clang_getCursorReferenced(child);
// stream is after the keyword
// syntax: = default
detail::skip(stream, "=");
std::string spelling;
while (!stream.done())
spelling += stream.get().c_str();
if (stream.unmunch())
{
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
spelling.pop_back();
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
}
builder.default_template(
cpp_template_ref(detail::get_entity_id(target), std::move(spelling)));
}
else
DEBUG_ASSERT(clang_isReference(kind), detail::parse_error_handler{}, cur,
"unexpected child of template template parameter");
});
return builder.finish(*context.idx, detail::get_entity_id(cur));
}
template <class Builder>
void parse_parameters(Builder& builder, const detail::parse_context& context,
const CXCursor& cur)
{
// now visit to get the parameters
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
});
}
void handle_comment_attributes(cpp_entity& templ_entity, cpp_entity& non_template)
{
// steal comment
auto comment = type_safe::copy(non_template.comment());
non_template.set_comment(type_safe::nullopt);
templ_entity.set_comment(std::move(comment));
// copy attributes over
templ_entity.add_attribute(non_template.attributes());
}
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(), keyword, variadic, std::move(def));
}
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = clang_getCursorType(cur);
cpp_attribute_list attributes;
auto def = detail::parse_default_value(attributes, context, cur, name.c_str());
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// see if it is variadic
// syntax a): some-tokens ... name some-tokens
// syntax b): some-tokens (some-tokens ... name) some-tokens-or-...
// name might be empty, so can't loop until it occurs
// some-tokens will not contain ... or parenthesis, however
auto is_variadic = false;
for (; !stream.done(); stream.bump())
{
if (stream.peek() == "...")
{
is_variadic = true;
break;
}
else if (stream.peek() == ")")
break;
}
auto result = cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(),
detail::parse_type(context, cur, type),
is_variadic, std::move(def));
result->add_attribute(attributes);
return std::move(result);
}
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter,
detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: template <…> class/typename [...] name [= …]
detail::skip(stream, "template");
detail::skip_brackets(stream);
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto is_variadic = detail::skip_if(stream, "...");
detail::skip(stream, name.c_str());
// now we can create the builder
cpp_template_template_parameter::builder builder(name.c_str(), is_variadic);
builder.keyword(keyword);
// look for parameters and default
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
else if (kind == CXCursor_TemplateRef)
{
auto target = clang_getCursorReferenced(child);
// stream is after the keyword
// syntax: = default
detail::skip(stream, "=");
std::string spelling;
while (!stream.done())
spelling += stream.get().c_str();
if (stream.unmunch())
{
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>', detail::assert_handler{});
spelling.pop_back();
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>', detail::assert_handler{});
}
builder.default_template(
cpp_template_ref(detail::get_entity_id(target), std::move(spelling)));
}
else
DEBUG_ASSERT(clang_isReference(kind), detail::parse_error_handler{}, cur,
"unexpected child of template template parameter");
});
return builder.finish(*context.idx, detail::get_entity_id(cur));
}
template <class Builder>
void parse_parameters(Builder& builder, const detail::parse_context& context, const CXCursor& cur)
{
// now visit to get the parameters
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
});
}
void handle_comment_attributes(cpp_entity& templ_entity, cpp_entity& non_template)
{
// steal comment
auto comment = type_safe::copy(non_template.comment());
non_template.set_comment(type_safe::nullopt);
templ_entity.set_comment(std::move(comment));
// copy attributes over
templ_entity.add_attribute(non_template.attributes());
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl,
detail::assert_handler{});
auto builder =
get_builder<cpp_alias_template, cpp_type_alias>(context, cur, [](cpp_entity_kind k) {
return k == cpp_entity_kind::type_alias_t;
});
auto builder
= get_builder<cpp_alias_template, cpp_type_alias>(context, cur, [](cpp_entity_kind k) {
return k == cpp_entity_kind::type_alias_t;
});
if (!builder)
return nullptr;
context.comments.match(builder.value().get(), cur);
@ -274,28 +269,27 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
namespace
{
template <class Builder>
void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur)
template <class Builder>
void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
while (!stream.done() && !detail::skip_if(stream, detail::get_cursor_name(cur).c_str(), true))
stream.bump();
if (stream.peek() == "<")
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto iter = detail::find_closing_bracket(stream);
stream.bump();
while (!stream.done()
&& !detail::skip_if(stream, detail::get_cursor_name(cur).c_str(), true))
stream.bump();
if (stream.peek() == "<")
{
auto iter = detail::find_closing_bracket(stream);
stream.bump();
auto args = detail::to_string(stream, iter);
b.add_unexposed_arguments(std::move(args));
}
else
b.add_unexposed_arguments(cpp_token_string::builder().finish());
auto args = detail::to_string(stream, iter);
b.add_unexposed_arguments(std::move(args));
}
else
b.add_unexposed_arguments(cpp_token_string::builder().finish());
}
} // namespace
std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specialization(
const detail::parse_context& context, const CXCursor& cur, bool is_friend)

File diff suppressed because it is too large Load diff

View file

@ -64,8 +64,8 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
for (auto& token : tokenizer)
if (token.value() == "thread_local")
storage_class =
cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local);
storage_class
= cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local);
else if (token.value() == "constexpr")
is_constexpr = true;
@ -75,9 +75,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
std::unique_ptr<cpp_variable> result;
if (clang_isCursorDefinition(cur))
{
result =
cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
std::move(default_value), storage_class, is_constexpr);
result
= cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
std::move(default_value), storage_class, is_constexpr);
}
else
result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type),

View file

@ -18,47 +18,46 @@ using namespace cppast;
namespace
{
cpp_access_specifier_kind get_initial_access(const cpp_entity& e)
{
if (e.kind() == cpp_class::kind())
return static_cast<const cpp_class&>(e).class_kind() == cpp_class_kind::class_t ?
cpp_private :
cpp_public;
return cpp_public;
}
cpp_access_specifier_kind get_initial_access(const cpp_entity& e)
{
if (e.kind() == cpp_class::kind())
return static_cast<const cpp_class&>(e).class_kind() == cpp_class_kind::class_t
? cpp_private
: cpp_public;
return cpp_public;
}
void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child)
{
if (child.kind() == cpp_access_specifier::kind())
child_access = static_cast<const cpp_access_specifier&>(child).access_specifier();
}
void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child)
{
if (child.kind() == cpp_access_specifier::kind())
child_access = static_cast<const cpp_access_specifier&>(child).access_specifier();
}
template <typename T>
bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor,
cpp_access_specifier_kind cur_access, bool last_child)
{
auto& container = static_cast<const T&>(e);
template <typename T>
bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor,
cpp_access_specifier_kind cur_access, bool last_child)
{
auto& container = static_cast<const T&>(e);
auto handle_children =
cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child});
if (handle_children)
auto handle_children
= cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child});
if (handle_children)
{
auto child_access = get_initial_access(e);
for (auto iter = container.begin(); iter != container.end();)
{
auto child_access = get_initial_access(e);
for (auto iter = container.begin(); iter != container.end();)
{
auto& cur = *iter;
++iter;
auto& cur = *iter;
++iter;
update_access(child_access, cur);
update_access(child_access, cur);
if (!detail::visit(cur, cb, functor, child_access, iter == container.end()))
return false;
}
if (!detail::visit(cur, cb, functor, child_access, iter == container.end()))
return false;
}
return cb(functor, container,
{visitor_info::container_entity_exit, cur_access, last_child});
}
return cb(functor, container, {visitor_info::container_entity_exit, cur_access, last_child});
}
} // namespace
bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor,