Parse cpp_function_template_specialization

This commit is contained in:
Jonathan Müller 2017-03-27 10:07:13 +02:00
commit bf7c85a9dd
19 changed files with 567 additions and 257 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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