Parse cpp_function_template_specialization
This commit is contained in:
parent
eeb48f1df5
commit
bf7c85a9dd
19 changed files with 567 additions and 257 deletions
|
|
@ -74,6 +74,9 @@ namespace cppast
|
|||
/// \notes A template template parameter is not considered a template for this function.
|
||||
/// \notes Template specializations are also considered templates here.
|
||||
bool is_template(cpp_entity_kind kind) noexcept;
|
||||
|
||||
/// \returns Whether or not a given entity kind is a specialization of a C++ template.
|
||||
bool is_template_specialization(cpp_entity_kind kind) noexcept;
|
||||
} // namespace cppast
|
||||
|
||||
#endif // CPPAST_CPP_ENTITY_KIND_HPP_INCLUDED
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ namespace cppast
|
|||
class cpp_function_template_specialization final : public cpp_template_specialization
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builder for [cppast::cpp_function_template_specialization]().
|
||||
class builder
|
||||
: public specialization_builder<cpp_function_template_specialization, cpp_function_base>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <type_safe/variant.hpp>
|
||||
|
||||
#include <cppast/cpp_entity.hpp>
|
||||
#include <cppast/cpp_entity_container.hpp>
|
||||
#include <cppast/cpp_template_parameter.hpp>
|
||||
|
|
@ -94,9 +96,18 @@ namespace cppast
|
|||
}
|
||||
|
||||
/// \effects Adds the next argument.
|
||||
/// \requires No call to `add_unexposed_arguments()` has happened before.
|
||||
void add_argument(cpp_template_argument arg)
|
||||
{
|
||||
result_->arguments_.push_back(std::move(arg));
|
||||
result_->arguments_
|
||||
.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
|
||||
.push_back(std::move(arg));
|
||||
}
|
||||
|
||||
/// \effects Adds unexposed arguments as string.
|
||||
void add_unexposed_arguments(std::string arg)
|
||||
{
|
||||
result_->arguments_ = std::move(arg);
|
||||
}
|
||||
|
||||
/// \returns The finished instantiation.
|
||||
|
|
@ -117,15 +128,33 @@ namespace cppast
|
|||
return templ_;
|
||||
}
|
||||
|
||||
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
|
||||
/// \exclude return
|
||||
const std::vector<cpp_template_argument>& arguments() const noexcept
|
||||
/// \returns Whether or not the arguments are exposed.
|
||||
bool arguments_exposed() const noexcept
|
||||
{
|
||||
return arguments_;
|
||||
return arguments_.has_value(
|
||||
type_safe::variant_type<std::vector<cpp_template_argument>>{});
|
||||
}
|
||||
|
||||
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
|
||||
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
|
||||
type_safe::array_ref<const cpp_template_argument> arguments() const noexcept
|
||||
{
|
||||
auto& vec =
|
||||
arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
|
||||
return type_safe::ref(vec.data(), vec.size());
|
||||
}
|
||||
|
||||
/// \returns The unexposed arguments as string.
|
||||
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
|
||||
const std::string& unexposed_arguments() const noexcept
|
||||
{
|
||||
return arguments_.value(type_safe::variant_type<std::string>{});
|
||||
}
|
||||
|
||||
private:
|
||||
cpp_template_instantiation_type(cpp_template_ref ref) : templ_(std::move(ref))
|
||||
cpp_template_instantiation_type(cpp_template_ref ref)
|
||||
: arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
|
||||
templ_(std::move(ref))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -134,8 +163,8 @@ namespace cppast
|
|||
return cpp_type_kind::template_instantiation;
|
||||
}
|
||||
|
||||
std::vector<cpp_template_argument> arguments_;
|
||||
cpp_template_ref templ_;
|
||||
type_safe::variant<std::vector<cpp_template_argument>, std::string> arguments_;
|
||||
cpp_template_ref templ_;
|
||||
};
|
||||
|
||||
/// Base class for all entities modelling a C++ template specialization.
|
||||
|
|
@ -148,11 +177,31 @@ namespace cppast
|
|||
return cpp_template_ref(templ_, name());
|
||||
}
|
||||
|
||||
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
|
||||
/// \exclude return
|
||||
const std::vector<cpp_template_argument>& arguments() const noexcept
|
||||
/// \returns Whether or not the arguments are exposed.
|
||||
bool arguments_exposed() const noexcept
|
||||
{
|
||||
return arguments_;
|
||||
return arguments_.has_value(
|
||||
type_safe::variant_type<std::vector<cpp_template_argument>>{});
|
||||
}
|
||||
|
||||
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
|
||||
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
|
||||
/// \notes For function template specializations it can be empty,
|
||||
/// meaning that the arguments are not explictly given but deduced from the signature.
|
||||
type_safe::array_ref<const cpp_template_argument> arguments() const noexcept
|
||||
{
|
||||
auto& vec =
|
||||
arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
|
||||
return type_safe::ref(vec.data(), vec.size());
|
||||
}
|
||||
|
||||
/// \returns The unexposed arguments as string.
|
||||
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
|
||||
/// \notes For function template specializations it can be empty,
|
||||
/// meaning that the arguments are not explictly given but deduced from the signature.
|
||||
const std::string& unexposed_arguments() const noexcept
|
||||
{
|
||||
return arguments_.value(type_safe::variant_type<std::string>{});
|
||||
}
|
||||
|
||||
/// \returns Whether or not the specialization is a full specialization.
|
||||
|
|
@ -177,11 +226,22 @@ namespace cppast
|
|||
}
|
||||
|
||||
/// \effects Adds the next argument for the [cppast::cpp_template_parameter]() of the primary template.
|
||||
/// \requires No call to `add_unexposed_arguments()` has happened before.
|
||||
void add_argument(cpp_template_argument arg)
|
||||
{
|
||||
auto specialization =
|
||||
static_cast<const cpp_template_specialization&>(*this->template_entity);
|
||||
specialization.arguments_.push_back(std::move(arg));
|
||||
auto& specialization =
|
||||
static_cast<cpp_template_specialization&>(*this->template_entity);
|
||||
specialization.arguments_
|
||||
.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
|
||||
.push_back(std::move(arg));
|
||||
}
|
||||
|
||||
/// \effects Adds unexposed arguments as string.
|
||||
void add_unexposed_arguments(std::string arg)
|
||||
{
|
||||
auto& specialization =
|
||||
static_cast<cpp_template_specialization&>(*this->template_entity);
|
||||
specialization.arguments_ = std::move(arg);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
@ -191,15 +251,18 @@ namespace cppast
|
|||
/// \effects Sets the entity that is being templated and the primary template.
|
||||
cpp_template_specialization(std::unique_ptr<cpp_entity> entity,
|
||||
const cpp_template_ref& templ)
|
||||
: cpp_template(std::move(entity)), templ_(templ.id()[0u])
|
||||
: cpp_template(std::move(entity)),
|
||||
arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
|
||||
templ_(templ.id()[0u])
|
||||
{
|
||||
DEBUG_ASSERT(!templ.is_overloaded() && templ.name() == entity->name(),
|
||||
DEBUG_ASSERT(!templ.is_overloaded()
|
||||
&& (templ.name().empty() || templ.name() == begin()->name()),
|
||||
detail::precondition_error_handler{}, "invalid name of template ref");
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<cpp_template_argument> arguments_;
|
||||
cpp_entity_id templ_;
|
||||
type_safe::variant<std::vector<cpp_template_argument>, std::string> arguments_;
|
||||
cpp_entity_id templ_;
|
||||
};
|
||||
} // namespace cppast
|
||||
|
||||
|
|
|
|||
|
|
@ -270,3 +270,47 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case cpp_entity_kind::function_template_specialization_t:
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
return true;
|
||||
|
||||
case cpp_entity_kind::file_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_t:
|
||||
case cpp_entity_kind::enum_value_t:
|
||||
case cpp_entity_kind::class_t:
|
||||
case cpp_entity_kind::access_specifier_t:
|
||||
case cpp_entity_kind::base_class_t:
|
||||
case cpp_entity_kind::variable_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::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:
|
||||
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::function_template_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::count:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,12 @@ cpp_entity_kind cpp_function_template::do_get_entity_kind() const noexcept
|
|||
return kind();
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_function_template_specialization::do_get_entity_kind() const noexcept
|
||||
cpp_entity_kind cpp_function_template_specialization::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::function_template_specialization_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_function_template_specialization::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return kind();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,6 @@
|
|||
|
||||
using namespace cppast;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string get_expression_str(detail::token_stream& stream, detail::token_iterator end)
|
||||
{
|
||||
// just concat everything
|
||||
std::string expr;
|
||||
while (stream.cur() != end)
|
||||
expr += stream.get().c_str();
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
|
|
@ -30,7 +18,7 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
|
|||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto type = parse_type(context, cur, clang_getCursorType(cur));
|
||||
auto expr = get_expression_str(stream, stream.end());
|
||||
auto expr = to_string(stream, stream.end());
|
||||
if (kind == CXCursor_CallExpr && (expr.empty() || expr.back() != ')'))
|
||||
{
|
||||
// we have a call expression that doesn't end in a closing parentheses
|
||||
|
|
@ -54,6 +42,6 @@ std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context
|
|||
{
|
||||
if (stream.done())
|
||||
return nullptr;
|
||||
auto expr = get_expression_str(stream, end);
|
||||
auto expr = to_string(stream, end);
|
||||
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,33 +55,97 @@ namespace
|
|||
});
|
||||
}
|
||||
|
||||
bool is_templated(const CXCursor& cur)
|
||||
{
|
||||
return clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound
|
||||
|| !clang_Cursor_isNull(clang_getSpecializedCursorTemplate(cur));
|
||||
}
|
||||
|
||||
// precondition: after the name
|
||||
void skip_parameters(detail::token_stream& stream)
|
||||
{
|
||||
if (stream.peek() == "<")
|
||||
// specialization arguments
|
||||
detail::skip_brackets(stream);
|
||||
detail::skip_brackets(stream);
|
||||
}
|
||||
|
||||
// just the tokens occurring in the prefix
|
||||
struct prefix_info
|
||||
{
|
||||
bool is_constexpr = false;
|
||||
bool is_virtual = false;
|
||||
std::string scope_name;
|
||||
bool is_constexpr = false;
|
||||
bool is_virtual = false;
|
||||
bool is_explicit = false;
|
||||
};
|
||||
|
||||
prefix_info parse_prefix_info(detail::token_stream& stream, const detail::cxstring& name)
|
||||
bool prefix_end(detail::token_stream& stream, const char* name)
|
||||
{
|
||||
auto cur = stream.cur();
|
||||
// name can have multiple tokens if it is an operator
|
||||
if (!detail::skip_if(stream, name, true))
|
||||
return false;
|
||||
else if (stream.peek() == "::")
|
||||
{
|
||||
// was a class name of constructor
|
||||
stream.set_cur(cur);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
prefix_info parse_prefix_info(detail::token_stream& stream, const char* name)
|
||||
{
|
||||
prefix_info result;
|
||||
|
||||
// just check for keywords until we've reached the function name
|
||||
// notes: name can have multiple tokens if it is an operator
|
||||
while (!detail::skip_if(stream, name.c_str(), true))
|
||||
std::string scope;
|
||||
while (!prefix_end(stream, name))
|
||||
{
|
||||
if (detail::skip_if(stream, "constexpr"))
|
||||
{
|
||||
result.is_constexpr = true;
|
||||
scope.clear();
|
||||
}
|
||||
else if (detail::skip_if(stream, "virtual"))
|
||||
{
|
||||
result.is_virtual = true;
|
||||
scope.clear();
|
||||
}
|
||||
else if (detail::skip_if(stream, "explicit"))
|
||||
{
|
||||
result.is_explicit = true;
|
||||
scope.clear();
|
||||
}
|
||||
// add identifiers and "::" to current scope name,
|
||||
// clear if there is any other token in between, or mismatched combination
|
||||
else if (stream.peek().kind() == CXToken_Identifier)
|
||||
{
|
||||
if (!scope.empty() && scope.back() != ':')
|
||||
scope.clear();
|
||||
scope += stream.get().c_str();
|
||||
}
|
||||
else if (stream.peek() == "::")
|
||||
{
|
||||
if (!scope.empty() && scope.back() == ':')
|
||||
scope.clear();
|
||||
scope += stream.get().c_str();
|
||||
}
|
||||
else if (stream.peek() == "<")
|
||||
{
|
||||
auto iter = detail::find_closing_bracket(stream);
|
||||
scope += detail::to_string(stream, iter);
|
||||
detail::skip(stream, ">");
|
||||
scope += ">";
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.bump();
|
||||
scope.clear();
|
||||
}
|
||||
}
|
||||
if (!scope.empty() && scope.back() == ':')
|
||||
result.scope_name = std::move(scope);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -275,11 +339,18 @@ namespace
|
|||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_cpp_function_impl(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
const CXCursor& cur, bool is_static)
|
||||
{
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
|
||||
cpp_function::builder builder(name.c_str(),
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
DEBUG_ASSERT(!prefix.is_virtual && !prefix.is_explicit, detail::parse_error_handler{}, cur,
|
||||
"free function cannot be virtual or explicit");
|
||||
|
||||
cpp_function::builder builder(prefix.scope_name + name.c_str(),
|
||||
detail::parse_type(context, cur,
|
||||
clang_getCursorResultType(cur)));
|
||||
context.comments.match(builder.get(), cur);
|
||||
|
|
@ -287,14 +358,9 @@ namespace
|
|||
add_parameters(context, builder, cur);
|
||||
if (clang_Cursor_isVariadic(cur))
|
||||
builder.is_variadic();
|
||||
builder.storage_class(detail::get_storage_class(cur));
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name);
|
||||
DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
|
||||
"free function cannot be virtual");
|
||||
builder.storage_class(cpp_storage_class_specifiers(
|
||||
detail::get_storage_class(cur)
|
||||
| (is_static ? cpp_storage_class_static : cpp_storage_class_none)));
|
||||
if (prefix.is_constexpr)
|
||||
builder.is_constexpr();
|
||||
|
||||
|
|
@ -304,10 +370,10 @@ namespace
|
|||
if (suffix.noexcept_condition)
|
||||
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||
|
||||
if (clang_getTemplateCursorKind(cur) == CXCursor_NoDeclFound)
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
else
|
||||
if (is_templated(cur))
|
||||
return builder.finish(suffix.body_kind);
|
||||
else
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -317,7 +383,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_conte
|
|||
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl
|
||||
|| clang_getTemplateCursorKind(cur) == CXCursor_FunctionDecl,
|
||||
detail::assert_handler{});
|
||||
return parse_cpp_function_impl(context, cur);
|
||||
return parse_cpp_function_impl(context, cur, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
|
||||
|
|
@ -327,7 +393,7 @@ std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
|
|||
|| clang_getTemplateCursorKind(cur) == CXCursor_CXXMethod,
|
||||
detail::assert_handler{});
|
||||
if (clang_CXXMethod_isStatic(cur))
|
||||
return parse_cpp_function_impl(context, cur);
|
||||
return parse_cpp_function_impl(context, cur, true);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +479,10 @@ namespace
|
|||
if (auto virt = calculate_virtual(cur, is_virtual, suffix.virtual_keywords))
|
||||
builder.virtual_info(virt.value());
|
||||
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
if (is_templated(cur))
|
||||
return builder.finish(suffix.body_kind);
|
||||
else
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -425,7 +494,14 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
|
|||
detail::assert_handler{});
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
|
||||
cpp_member_function::builder builder(name.c_str(),
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
DEBUG_ASSERT(!prefix.is_explicit, detail::parse_error_handler{}, cur,
|
||||
"member function cannot be explicit");
|
||||
|
||||
cpp_member_function::builder builder(prefix.scope_name + name.c_str(),
|
||||
detail::parse_type(context, cur,
|
||||
clang_getCursorResultType(cur)));
|
||||
context.comments.match(builder.get(), cur);
|
||||
|
|
@ -433,10 +509,6 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
|
|||
if (clang_Cursor_isVariadic(cur))
|
||||
builder.is_variadic();
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name);
|
||||
if (prefix.is_constexpr)
|
||||
builder.is_constexpr();
|
||||
|
||||
|
|
@ -451,32 +523,13 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
|
|||
|| clang_getTemplateCursorKind(cur) == CXCursor_ConversionFunction,
|
||||
detail::assert_handler{});
|
||||
|
||||
auto type = clang_getCursorResultType(cur);
|
||||
cpp_conversion_op::builder builder(std::string("operator ")
|
||||
+ cxstring(clang_getTypeSpelling(type)).c_str(),
|
||||
detail::parse_type(context, cur, type));
|
||||
context.comments.match(builder.get(), cur);
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
// look for constexpr, explicit, virtual
|
||||
// must come before the operator token
|
||||
auto is_virtual = false;
|
||||
while (!detail::skip_if(stream, "operator"))
|
||||
{
|
||||
if (detail::skip_if(stream, "virtual"))
|
||||
is_virtual = true;
|
||||
else if (detail::skip_if(stream, "constexpr"))
|
||||
builder.is_constexpr();
|
||||
else if (detail::skip_if(stream, "explicit"))
|
||||
builder.is_explicit();
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
|
||||
auto prefix = parse_prefix_info(stream, "operator");
|
||||
// heuristic to find arguments tokens
|
||||
// skip forward, skipping inside brackets
|
||||
auto type_start = stream.cur();
|
||||
while (true)
|
||||
{
|
||||
if (detail::skip_if(stream, "("))
|
||||
|
|
@ -495,8 +548,29 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
|
|||
else
|
||||
stream.bump();
|
||||
}
|
||||
// bump arguments back
|
||||
stream.bump_back();
|
||||
stream.bump_back();
|
||||
auto type_end = stream.cur();
|
||||
|
||||
return handle_suffix(context, cur, builder, stream, is_virtual);
|
||||
// read the type
|
||||
stream.set_cur(type_start);
|
||||
auto type_spelling = detail::to_string(stream, type_end);
|
||||
|
||||
// parse arguments again
|
||||
detail::skip(stream, "(");
|
||||
detail::skip(stream, ")");
|
||||
|
||||
auto type = clang_getCursorResultType(cur);
|
||||
cpp_conversion_op::builder builder(prefix.scope_name + "operator " + type_spelling,
|
||||
detail::parse_type(context, cur, type));
|
||||
context.comments.match(builder.get(), cur);
|
||||
if (prefix.is_explicit)
|
||||
builder.is_explicit();
|
||||
else if (prefix.is_constexpr)
|
||||
builder.is_constexpr();
|
||||
|
||||
return handle_suffix(context, cur, builder, stream, prefix.is_virtual);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_context& context,
|
||||
|
|
@ -507,25 +581,22 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
|
|||
detail::assert_handler{});
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
|
||||
cpp_constructor::builder builder(name.c_str());
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
auto prefix = parse_prefix_info(stream, name.c_str());
|
||||
DEBUG_ASSERT(!prefix.is_virtual, detail::parse_error_handler{}, cur,
|
||||
"constructor cannot be virtual");
|
||||
|
||||
cpp_constructor::builder builder(prefix.scope_name + name.c_str());
|
||||
context.comments.match(builder.get(), cur);
|
||||
add_parameters(context, builder, cur);
|
||||
if (clang_Cursor_isVariadic(cur))
|
||||
builder.is_variadic();
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
// parse prefix
|
||||
while (!detail::skip_if(stream, name.c_str()))
|
||||
{
|
||||
if (detail::skip_if(stream, "constexpr"))
|
||||
builder.is_constexpr();
|
||||
else if (detail::skip_if(stream, "explicit"))
|
||||
builder.is_explicit();
|
||||
else
|
||||
stream.bump();
|
||||
}
|
||||
if (prefix.is_constexpr)
|
||||
builder.is_constexpr();
|
||||
else if (prefix.is_explicit)
|
||||
builder.is_explicit();
|
||||
|
||||
skip_parameters(stream);
|
||||
|
||||
|
|
@ -533,7 +604,10 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
|
|||
if (suffix.noexcept_condition)
|
||||
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
if (is_templated(cur))
|
||||
return builder.finish(suffix.body_kind);
|
||||
else
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_destructor(const detail::parse_context& context,
|
||||
|
|
|
|||
|
|
@ -108,15 +108,22 @@ 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))
|
||||
return tfunc;
|
||||
return parse_cpp_function(context, cur);
|
||||
case CXCursor_CXXMethod:
|
||||
// check for static function
|
||||
if (auto func = try_parse_static_cpp_function(context, cur))
|
||||
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
|
||||
return tfunc;
|
||||
else if (auto func = try_parse_static_cpp_function(context, cur))
|
||||
return func;
|
||||
return parse_cpp_member_function(context, cur);
|
||||
case CXCursor_ConversionFunction:
|
||||
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
|
||||
return tfunc;
|
||||
return parse_cpp_conversion_op(context, cur);
|
||||
case CXCursor_Constructor:
|
||||
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
|
||||
return tfunc;
|
||||
return parse_cpp_constructor(context, cur);
|
||||
case CXCursor_Destructor:
|
||||
return parse_cpp_destructor(context, cur);
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ namespace cppast
|
|||
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);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
|
||||
|
|
|
|||
|
|
@ -261,3 +261,69 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
|
|||
parse_parameters(builder, context, cur);
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class Builder>
|
||||
void add_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur)
|
||||
{
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
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("");
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specialization(
|
||||
const detail::parse_context& context, const CXCursor& cur)
|
||||
{
|
||||
auto templ = clang_getSpecializedCursorTemplate(cur);
|
||||
if (clang_Cursor_isNull(templ))
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<cpp_entity> func;
|
||||
switch (clang_getCursorKind(cur))
|
||||
{
|
||||
case CXCursor_FunctionDecl:
|
||||
func = detail::parse_cpp_function(context, cur);
|
||||
break;
|
||||
case CXCursor_CXXMethod:
|
||||
if (auto sfunc = detail::try_parse_static_cpp_function(context, cur))
|
||||
func = std::move(sfunc);
|
||||
else
|
||||
func = detail::parse_cpp_member_function(context, cur);
|
||||
break;
|
||||
case CXCursor_ConversionFunction:
|
||||
func = detail::parse_cpp_conversion_op(context, cur);
|
||||
break;
|
||||
case CXCursor_Constructor:
|
||||
func = detail::parse_cpp_constructor(context, cur);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
|
||||
"unexpected function kind that is being specialized");
|
||||
}
|
||||
if (!func)
|
||||
return nullptr;
|
||||
|
||||
cpp_function_template_specialization::builder
|
||||
builder(std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())),
|
||||
cpp_template_ref(detail::get_entity_id(templ), ""));
|
||||
|
||||
add_arguments(builder, context, cur);
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -332,3 +332,30 @@ bool detail::skip_attribute(detail::token_stream& stream)
|
|||
any = true;
|
||||
return any;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_identifier(char c)
|
||||
{
|
||||
return std::isalnum(c) || c == '_';
|
||||
}
|
||||
}
|
||||
|
||||
std::string detail::to_string(token_stream& stream, token_iterator end)
|
||||
{
|
||||
std::string result;
|
||||
while (stream.cur() != end)
|
||||
{
|
||||
auto& token = stream.get();
|
||||
if (!result.empty() && is_identifier(result.back()) && is_identifier(token.value()[0u]))
|
||||
result += ' ';
|
||||
result += token.c_str();
|
||||
}
|
||||
if (stream.unmunch())
|
||||
{
|
||||
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
|
||||
result.pop_back();
|
||||
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@ namespace cppast
|
|||
|
||||
// skips an attribute
|
||||
bool skip_attribute(token_stream& stream);
|
||||
|
||||
// converts a token range to a string, adding whitespace where necessary
|
||||
std::string to_string(token_stream& stream, token_iterator end);
|
||||
}
|
||||
} // namespace cppast::detail
|
||||
|
||||
|
|
|
|||
|
|
@ -385,73 +385,6 @@ namespace
|
|||
type);
|
||||
}
|
||||
|
||||
const char* find_closing_bracket(const char* ptr)
|
||||
{
|
||||
for (auto paren_count = 0; *ptr; ++ptr)
|
||||
{
|
||||
if (*ptr == '(' || *ptr == '[' || *ptr == '{')
|
||||
++paren_count;
|
||||
else if (*ptr == ')' || *ptr == ']' || *ptr == '}')
|
||||
--paren_count;
|
||||
else if (paren_count == 0 && *ptr == '>' && !std::isalnum(*ptr) && *ptr != '_')
|
||||
// heuristic: this could be in fact a closing bracket
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string parse_argument(const CXCursor& cur, const char*& ptr, bool is_expression)
|
||||
{
|
||||
std::string arg;
|
||||
|
||||
auto paren_count = 0; // non angle brackets
|
||||
auto bracket_count = 0; // angle brackets
|
||||
while (*ptr && ((paren_count + bracket_count) != 0 || *ptr != ','))
|
||||
{
|
||||
if (*ptr == '(' || *ptr == '[' || *ptr == '{')
|
||||
++paren_count;
|
||||
else if (*ptr == ')' || *ptr == ']' || *ptr == '}')
|
||||
--paren_count;
|
||||
// angle brackets are tricky
|
||||
// as they can be both brackets and operators
|
||||
// we only need to take care of those that aren't nested inside other brackets, luckily
|
||||
else if (paren_count == 0)
|
||||
{
|
||||
if (is_expression && *ptr == '<')
|
||||
{
|
||||
// treat as brackets and see if it got a closing one
|
||||
auto closing = find_closing_bracket(ptr);
|
||||
if (closing)
|
||||
{
|
||||
// assume this is a closing bracket
|
||||
while (ptr != closing)
|
||||
arg += *ptr++;
|
||||
}
|
||||
}
|
||||
// not an expression
|
||||
// all top-level angle brackets are actually brackets
|
||||
else if (*ptr == '<')
|
||||
++bracket_count;
|
||||
else if (*ptr == '>')
|
||||
--bracket_count;
|
||||
}
|
||||
|
||||
arg += *ptr++;
|
||||
}
|
||||
|
||||
DEBUG_ASSERT(*ptr == ',', detail::parse_error_handler{}, cur,
|
||||
"unable to parse template argument");
|
||||
++ptr;
|
||||
|
||||
while (*ptr == ' ')
|
||||
++ptr;
|
||||
while (!arg.empty() && arg.back() == ' ')
|
||||
arg.pop_back();
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
CXCursor get_instantiation_template(const CXCursor& cur, const CXType& type,
|
||||
const std::string& templ_name)
|
||||
{
|
||||
|
|
@ -474,11 +407,10 @@ namespace
|
|||
return param;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> try_parse_instantiation_type(const detail::parse_context& context,
|
||||
std::unique_ptr<cpp_type> try_parse_instantiation_type(const detail::parse_context&,
|
||||
const CXCursor& cur, const CXType& type)
|
||||
{
|
||||
return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr<cpp_type> {
|
||||
spelling.back() = ','; // to easily terminate the last argument
|
||||
auto ptr = spelling.c_str();
|
||||
std::string templ_name;
|
||||
for (; *ptr && *ptr != '<'; ++ptr)
|
||||
|
|
@ -493,32 +425,10 @@ namespace
|
|||
cpp_template_ref(detail::get_entity_id(templ), std::move(templ_name)));
|
||||
|
||||
// parse arguments
|
||||
// visit children of declaration to get the kind of argument expected
|
||||
// then parse the string
|
||||
detail::visit_children(templ, [&](const CXCursor& child) {
|
||||
if (!*ptr)
|
||||
return;
|
||||
|
||||
auto kind = clang_getCursorKind(child);
|
||||
if (kind == CXCursor_TemplateTypeParameter)
|
||||
{
|
||||
auto arg = parse_argument(cur, ptr, false);
|
||||
builder.add_argument(
|
||||
std::unique_ptr<cpp_type>(cpp_unexposed_type::build(std::move(arg))));
|
||||
}
|
||||
else if (kind == CXCursor_NonTypeTemplateParameter)
|
||||
{
|
||||
auto arg = parse_argument(cur, ptr, true);
|
||||
auto arg_type = detail::parse_type(context, child, clang_getCursorType(child));
|
||||
builder.add_argument(std::unique_ptr<cpp_expression>(
|
||||
cpp_unexposed_expression::build(std::move(arg_type), std::move(arg))));
|
||||
}
|
||||
else if (kind == CXCursor_TemplateTemplateParameter)
|
||||
{
|
||||
auto arg = parse_argument(cur, ptr, false);
|
||||
builder.add_argument(cpp_template_ref(cpp_entity_id(""), std::move(arg)));
|
||||
}
|
||||
});
|
||||
// i.e. not parse really, just add the string
|
||||
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>', detail::assert_handler{});
|
||||
spelling.pop_back();
|
||||
builder.add_unexposed_arguments(ptr);
|
||||
|
||||
return builder.finish();
|
||||
});
|
||||
|
|
@ -655,32 +565,11 @@ std::unique_ptr<cpp_type> detail::parse_type(const detail::parse_context& contex
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_identifier(char c)
|
||||
{
|
||||
return std::isalnum(c) || c == '_';
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> detail::parse_raw_type(const detail::parse_context&,
|
||||
detail::token_stream& stream,
|
||||
detail::token_iterator end)
|
||||
{
|
||||
std::string result;
|
||||
while (stream.cur() != end)
|
||||
{
|
||||
auto& token = stream.get();
|
||||
if (!result.empty() && is_identifier(result.back()) && is_identifier(token.value()[0u]))
|
||||
result += ' ';
|
||||
result += token.c_str();
|
||||
}
|
||||
if (stream.unmunch())
|
||||
{
|
||||
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
|
||||
result.pop_back();
|
||||
DEBUG_ASSERT(!result.empty() && result.back() == '>', detail::assert_handler{});
|
||||
}
|
||||
auto result = detail::to_string(stream, end);
|
||||
return cpp_unexposed_type::build(std::move(result));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ using h = g<T, a>;
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "a"));
|
||||
builder.add_argument(cpp_unexposed_type::build("void"));
|
||||
builder.add_unexposed_arguments("void");
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish()));
|
||||
}
|
||||
else if (alias.name() == "e")
|
||||
|
|
@ -79,8 +79,7 @@ using h = g<T, a>;
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "b"));
|
||||
builder.add_argument(
|
||||
cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "I"));
|
||||
builder.add_unexposed_arguments("I");
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_cv_qualified_type::build(builder.finish(), cpp_cv_const)));
|
||||
}
|
||||
|
|
@ -91,9 +90,7 @@ using h = g<T, a>;
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "b"));
|
||||
builder.add_argument(cpp_unexposed_expression::build(cpp_builtin_type::build("int"),
|
||||
"I < a<int>{(0 , 1)}"));
|
||||
builder.add_argument(cpp_unexposed_type::build("int"));
|
||||
builder.add_unexposed_arguments("I < a<int>{(0 , 1)}, int");
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish()));
|
||||
}
|
||||
else if (alias.name() == "g")
|
||||
|
|
@ -104,7 +101,7 @@ using h = g<T, a>;
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "Templ"));
|
||||
builder.add_argument(cpp_unexposed_type::build("T"));
|
||||
builder.add_unexposed_arguments("T");
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish()));
|
||||
}
|
||||
else if (alias.name() == "h")
|
||||
|
|
@ -113,8 +110,7 @@ using h = g<T, a>;
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "g"));
|
||||
builder.add_argument(cpp_unexposed_type::build("T"));
|
||||
builder.add_argument(cpp_template_ref(cpp_entity_id("magic-allow-empty"), "a"));
|
||||
builder.add_unexposed_arguments("T, a");
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(), *builder.finish()));
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -29,8 +29,14 @@ constexpr void i();
|
|||
static constexpr void j();
|
||||
|
||||
// body
|
||||
void k() = delete;
|
||||
void l()
|
||||
namespace ns
|
||||
{
|
||||
void k() = delete;
|
||||
|
||||
void l();
|
||||
}
|
||||
|
||||
void ns::l()
|
||||
{
|
||||
// might confuse parser
|
||||
auto b = noexcept(g());
|
||||
|
|
@ -174,7 +180,7 @@ void l()
|
|||
REQUIRE(func.storage_class() == cpp_storage_class_static);
|
||||
}
|
||||
}
|
||||
else if (func.name() == "k" || func.name() == "l")
|
||||
else if (func.name() == "k" || func.name() == "l" || func.name() == "ns::l")
|
||||
{
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("void")));
|
||||
REQUIRE(count_children(func) == 0u);
|
||||
|
|
@ -186,12 +192,14 @@ void l()
|
|||
if (func.name() == "k")
|
||||
check_body(func, cpp_function_deleted);
|
||||
else if (func.name() == "l")
|
||||
check_body(func, cpp_function_declaration);
|
||||
else if (func.name() == "ns::l")
|
||||
check_body(func, cpp_function_definition);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 12u);
|
||||
REQUIRE(count == 13u);
|
||||
}
|
||||
|
||||
TEST_CASE("static cpp_function")
|
||||
|
|
@ -206,6 +214,8 @@ struct foo
|
|||
|
||||
static constexpr char c() = delete;
|
||||
};
|
||||
|
||||
void foo::a() {}
|
||||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
|
|
@ -215,12 +225,16 @@ struct foo
|
|||
REQUIRE(count_children(func) == 0u);
|
||||
REQUIRE(func.storage_class() == cpp_storage_class_static);
|
||||
|
||||
if (func.name() == "a")
|
||||
if (func.name() == "a" || func.name() == "foo::a")
|
||||
{
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("void")));
|
||||
REQUIRE(!func.noexcept_condition());
|
||||
REQUIRE(!func.is_constexpr());
|
||||
REQUIRE(func.body_kind() == cpp_function_declaration);
|
||||
|
||||
if (func.name() == "a")
|
||||
REQUIRE(func.body_kind() == cpp_function_declaration);
|
||||
else
|
||||
REQUIRE(func.body_kind() == cpp_function_definition);
|
||||
}
|
||||
else if (func.name() == "b")
|
||||
{
|
||||
|
|
@ -243,7 +257,7 @@ struct foo
|
|||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 3u);
|
||||
REQUIRE(count == 4u);
|
||||
}
|
||||
|
||||
// TODO: friend functions (clang 4.0 required)
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ TEST_CASE("cpp_function_template")
|
|||
{
|
||||
// only check templated related stuff
|
||||
auto code = R"(
|
||||
template <typename T>
|
||||
T a(const T& t);
|
||||
|
||||
template <int I>
|
||||
using type = int;
|
||||
|
||||
template <typename T>
|
||||
T a(const T& t);
|
||||
|
||||
struct d
|
||||
{
|
||||
template <int I, typename T>
|
||||
|
|
@ -34,6 +34,21 @@ struct d
|
|||
template <typename T>
|
||||
d(const T&);
|
||||
};
|
||||
|
||||
template <>
|
||||
int a(const int& t);
|
||||
|
||||
template <>
|
||||
type<0> d::b<0, int>(int);
|
||||
|
||||
template <>
|
||||
auto d::c() -> int;
|
||||
|
||||
template <>
|
||||
d::operator int() const;
|
||||
|
||||
template <>
|
||||
d::d(const int&);
|
||||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
|
|
@ -75,8 +90,7 @@ struct d
|
|||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "type"));
|
||||
builder.add_argument(
|
||||
cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "I"));
|
||||
builder.add_unexposed_arguments("I");
|
||||
REQUIRE(equal_types(idx, func.return_type(), *builder.finish()));
|
||||
|
||||
auto type_parameter = cpp_template_type_parameter_ref(cpp_entity_id(""), "T");
|
||||
|
|
@ -91,6 +105,7 @@ struct d
|
|||
}
|
||||
else if (tfunc.name() == "c")
|
||||
{
|
||||
check_parent(tfunc, "d", "d::c");
|
||||
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::member_function_t);
|
||||
|
|
@ -103,6 +118,7 @@ struct d
|
|||
}
|
||||
else if (tfunc.name() == "operator T")
|
||||
{
|
||||
check_parent(tfunc, "d", "d::operator T");
|
||||
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::conversion_op_t);
|
||||
|
|
@ -115,6 +131,7 @@ struct d
|
|||
}
|
||||
else if (tfunc.name() == "d")
|
||||
{
|
||||
check_parent(tfunc, "d", "d::d");
|
||||
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::constructor_t);
|
||||
|
|
@ -139,4 +156,105 @@ struct d
|
|||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 5u);
|
||||
|
||||
count = test_visit<cpp_function_template_specialization>(
|
||||
*file, [&](const cpp_function_template_specialization& tfunc) {
|
||||
REQUIRE(tfunc.is_full_specialization());
|
||||
REQUIRE(!tfunc.arguments_exposed());
|
||||
|
||||
auto templ = tfunc.primary_template();
|
||||
if (tfunc.name() == "d::operator int")
|
||||
REQUIRE(equal_ref(idx, templ, cpp_template_ref(cpp_entity_id(""), tfunc.name()),
|
||||
"d::operator T"));
|
||||
else
|
||||
REQUIRE(equal_ref(idx, templ, cpp_template_ref(cpp_entity_id(""), tfunc.name())));
|
||||
|
||||
if (tfunc.name() == "a")
|
||||
{
|
||||
REQUIRE(tfunc.unexposed_arguments() == "");
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::function_t);
|
||||
auto& func = static_cast<const cpp_function&>(tfunc.function());
|
||||
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("int")));
|
||||
|
||||
auto count = 0u;
|
||||
for (auto& param : func)
|
||||
{
|
||||
++count;
|
||||
REQUIRE(
|
||||
equal_types(idx, param.type(),
|
||||
*cpp_reference_type::
|
||||
build(cpp_cv_qualified_type::build(cpp_builtin_type::build(
|
||||
"int"),
|
||||
cpp_cv_const),
|
||||
cpp_ref_lvalue)));
|
||||
}
|
||||
REQUIRE(count == 1u);
|
||||
}
|
||||
else if (tfunc.name() == "d::b")
|
||||
{
|
||||
REQUIRE(tfunc.unexposed_arguments() == "0,int");
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::function_t);
|
||||
auto& func = static_cast<const cpp_function&>(tfunc.function());
|
||||
|
||||
cpp_template_instantiation_type::builder builder(
|
||||
cpp_template_ref(cpp_entity_id(""), "type"));
|
||||
builder.add_unexposed_arguments("0");
|
||||
REQUIRE(equal_types(idx, func.return_type(), *builder.finish()));
|
||||
|
||||
auto count = 0u;
|
||||
for (auto& param : func)
|
||||
{
|
||||
++count;
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int")));
|
||||
}
|
||||
REQUIRE(count == 1u);
|
||||
}
|
||||
else if (tfunc.name() == "d::c")
|
||||
{
|
||||
REQUIRE(tfunc.unexposed_arguments() == "");
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::member_function_t);
|
||||
auto& func = static_cast<const cpp_member_function&>(tfunc.function());
|
||||
REQUIRE(func.cv_qualifier() == cpp_cv_none);
|
||||
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("int")));
|
||||
}
|
||||
else if (tfunc.name() == "d::operator int")
|
||||
{
|
||||
REQUIRE(tfunc.unexposed_arguments() == "");
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::conversion_op_t);
|
||||
auto& func = static_cast<const cpp_conversion_op&>(tfunc.function());
|
||||
REQUIRE(func.cv_qualifier() == cpp_cv_const);
|
||||
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build("int")));
|
||||
}
|
||||
else if (tfunc.name() == "d::d")
|
||||
{
|
||||
REQUIRE(tfunc.unexposed_arguments() == "");
|
||||
|
||||
REQUIRE(tfunc.function().kind() == cpp_entity_kind::constructor_t);
|
||||
auto& func = static_cast<const cpp_constructor&>(tfunc.function());
|
||||
|
||||
auto count = 0u;
|
||||
for (auto& param : func)
|
||||
{
|
||||
++count;
|
||||
REQUIRE(
|
||||
equal_types(idx, param.type(),
|
||||
*cpp_reference_type::
|
||||
build(cpp_cv_qualified_type::build(cpp_builtin_type::build(
|
||||
"int"),
|
||||
cpp_cv_const),
|
||||
cpp_ref_lvalue)));
|
||||
}
|
||||
REQUIRE(count == 1u);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 5u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ struct foo
|
|||
|
||||
if (!op.is_explicit() && !op.is_constexpr())
|
||||
{
|
||||
REQUIRE(op.name() == "operator int &");
|
||||
REQUIRE(op.name() == "operator int&");
|
||||
REQUIRE(equal_types(idx, op.return_type(),
|
||||
*cpp_reference_type::build(cpp_builtin_type::build("int"),
|
||||
cpp_ref_lvalue)));
|
||||
|
|
|
|||
|
|
@ -13,23 +13,6 @@
|
|||
|
||||
using namespace cppast;
|
||||
|
||||
template <typename T, class Predicate>
|
||||
bool equal_ref(const cpp_entity_index& idx, const basic_cpp_entity_ref<T, Predicate>& parsed,
|
||||
const basic_cpp_entity_ref<T, Predicate>& synthesized)
|
||||
{
|
||||
if (parsed.name() != synthesized.name())
|
||||
return false;
|
||||
else if (parsed.is_overloaded() != synthesized.is_overloaded())
|
||||
return false;
|
||||
else if (parsed.is_overloaded())
|
||||
return false;
|
||||
|
||||
auto entities = parsed.get(idx);
|
||||
if (entities.size() != 1u)
|
||||
return synthesized.id()[0u] == cpp_entity_id("magic-allow-empty");
|
||||
return entities[0u]->name().empty() || full_name(*entities[0u]) == parsed.name();
|
||||
}
|
||||
|
||||
bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_type& synthesized)
|
||||
{
|
||||
if (parsed.kind() != synthesized.kind())
|
||||
|
|
@ -153,6 +136,10 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
|
||||
if (!equal_ref(idx, inst_parsed.primary_template(), inst_synthesized.primary_template()))
|
||||
return false;
|
||||
else if (inst_parsed.arguments_exposed() != inst_synthesized.arguments_exposed())
|
||||
return false;
|
||||
else if (!inst_parsed.arguments_exposed())
|
||||
return inst_parsed.unexposed_arguments() == inst_synthesized.unexposed_arguments();
|
||||
|
||||
auto iter_a = inst_parsed.arguments().begin();
|
||||
auto iter_b = inst_synthesized.arguments().begin();
|
||||
|
|
|
|||
|
|
@ -100,6 +100,26 @@ inline bool equal_expressions(const cppast::cpp_expression& parsed,
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename T, class Predicate>
|
||||
bool equal_ref(const cppast::cpp_entity_index& idx,
|
||||
const cppast::basic_cpp_entity_ref<T, Predicate>& parsed,
|
||||
const cppast::basic_cpp_entity_ref<T, Predicate>& synthesized,
|
||||
const char* full_name_override = nullptr)
|
||||
{
|
||||
if (parsed.name() != synthesized.name())
|
||||
return false;
|
||||
else if (parsed.is_overloaded() != synthesized.is_overloaded())
|
||||
return false;
|
||||
else if (parsed.is_overloaded())
|
||||
return false;
|
||||
|
||||
auto entities = parsed.get(idx);
|
||||
if (entities.size() != 1u)
|
||||
return false;
|
||||
return entities[0u]->name().empty()
|
||||
|| full_name(*entities[0u]) == (full_name_override ? full_name_override : parsed.name());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void check_template_parameters(
|
||||
const T& templ, std::initializer_list<std::pair<cppast::cpp_entity_kind, const char*>> params)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue