Implement attribute parsing
This commit is contained in:
parent
151e0b1a81
commit
ccb2aaa189
15 changed files with 512 additions and 86 deletions
|
|
@ -17,6 +17,8 @@ namespace cppast
|
||||||
/// The known C++ attributes.
|
/// The known C++ attributes.
|
||||||
enum class cpp_attribute_kind
|
enum class cpp_attribute_kind
|
||||||
{
|
{
|
||||||
|
// update get_attribute_kind() in tokenizer, when updating this
|
||||||
|
|
||||||
alignas_,
|
alignas_,
|
||||||
carries_dependency,
|
carries_dependency,
|
||||||
deprecated,
|
deprecated,
|
||||||
|
|
@ -52,6 +54,8 @@ namespace cppast
|
||||||
case cpp_attribute_kind::unknown:
|
case cpp_attribute_kind::unknown:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return "<error>";
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,17 +144,19 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \effects Builds a [cppast::cpp_base_class]() and adds it.
|
/// \effects Builds a [cppast::cpp_base_class]() and adds it.
|
||||||
void base_class(std::string name, std::unique_ptr<cpp_type> type,
|
cpp_base_class& base_class(std::string name, std::unique_ptr<cpp_type> type,
|
||||||
cpp_access_specifier_kind access, bool is_virtual)
|
cpp_access_specifier_kind access, bool is_virtual)
|
||||||
{
|
{
|
||||||
add_base_class(
|
return add_base_class(
|
||||||
cpp_base_class::build(std::move(name), std::move(type), access, is_virtual));
|
cpp_base_class::build(std::move(name), std::move(type), access, is_virtual));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \effects Adds a new base class.
|
/// \effects Adds a new base class.
|
||||||
void add_base_class(std::unique_ptr<cpp_base_class> base) noexcept
|
cpp_base_class& add_base_class(std::unique_ptr<cpp_base_class> base) noexcept
|
||||||
{
|
{
|
||||||
|
auto bptr = base.get();
|
||||||
class_->bases_.push_back(*class_, std::move(base));
|
class_->bases_.push_back(*class_, std::move(base));
|
||||||
|
return *bptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \effects Builds a [cppast::cpp_access_specifier]() and adds it.
|
/// \effects Builds a [cppast::cpp_access_specifier]() and adds it.
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,12 @@ namespace cppast
|
||||||
attributes_.push_back(std::move(attr));
|
attributes_.push_back(std::move(attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \effects Adds multiple arguments for that entity.
|
||||||
|
void add_attribute(const cpp_attribute_list& list) noexcept
|
||||||
|
{
|
||||||
|
attributes_.insert(attributes_.end(), list.begin(), list.end());
|
||||||
|
}
|
||||||
|
|
||||||
/// \returns The specified user data.
|
/// \returns The specified user data.
|
||||||
void* user_data() const noexcept
|
void* user_data() const noexcept
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,28 @@ using namespace cppast;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
cpp_class_kind parse_class_kind(const CXCursor& cur)
|
cpp_class_kind parse_class_kind(detail::cxtoken_stream& stream)
|
||||||
{
|
{
|
||||||
auto kind = clang_getTemplateCursorKind(cur);
|
auto kind = clang_getTemplateCursorKind(stream.cursor());
|
||||||
if (kind == CXCursor_NoDeclFound)
|
if (kind == CXCursor_NoDeclFound)
|
||||||
kind = clang_getCursorKind(cur);
|
kind = clang_getCursorKind(stream.cursor());
|
||||||
|
|
||||||
|
if (detail::skip_if(stream, "template"))
|
||||||
|
// skip template parameters
|
||||||
|
detail::skip_brackets(stream);
|
||||||
|
|
||||||
|
detail::skip_if(stream, "friend");
|
||||||
|
|
||||||
switch (kind)
|
switch (kind)
|
||||||
{
|
{
|
||||||
case CXCursor_ClassDecl:
|
case CXCursor_ClassDecl:
|
||||||
|
detail::skip(stream, "class");
|
||||||
return cpp_class_kind::class_t;
|
return cpp_class_kind::class_t;
|
||||||
case CXCursor_StructDecl:
|
case CXCursor_StructDecl:
|
||||||
|
detail::skip(stream, "struct");
|
||||||
return cpp_class_kind::struct_t;
|
return cpp_class_kind::struct_t;
|
||||||
case CXCursor_UnionDecl:
|
case CXCursor_UnionDecl:
|
||||||
|
detail::skip(stream, "union");
|
||||||
return cpp_class_kind::union_t;
|
return cpp_class_kind::union_t;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -32,11 +42,18 @@ namespace
|
||||||
return cpp_class_kind::class_t;
|
return cpp_class_kind::class_t;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpp_class::builder make_class_builder(const CXCursor& cur)
|
cpp_class::builder make_class_builder(const detail::parse_context& context, const CXCursor& cur)
|
||||||
{
|
{
|
||||||
auto kind = parse_class_kind(cur);
|
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
auto name = detail::get_cursor_name(cur);
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
return cpp_class::builder(name.c_str(), kind);
|
|
||||||
|
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)
|
cpp_access_specifier_kind convert_access(const CXCursor& cur)
|
||||||
|
|
@ -76,15 +93,16 @@ namespace
|
||||||
|
|
||||||
// [<attribute>] [virtual] [<access>] <name>
|
// [<attribute>] [virtual] [<access>] <name>
|
||||||
// can't use spelling to get the name
|
// can't use spelling to get the name
|
||||||
detail::skip_attribute(stream);
|
auto attributes = detail::parse_attributes(stream);
|
||||||
if (is_virtual)
|
if (is_virtual)
|
||||||
detail::skip(stream, "virtual");
|
detail::skip(stream, "virtual");
|
||||||
detail::skip_if(stream, to_string(access));
|
detail::skip_if(stream, to_string(access));
|
||||||
|
|
||||||
auto name = detail::to_string(stream, stream.end()).as_string();
|
auto name = detail::to_string(stream, stream.end()).as_string();
|
||||||
|
|
||||||
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
|
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
|
||||||
builder.base_class(std::move(name), std::move(type), access, is_virtual);
|
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);
|
||||||
|
base.add_attribute(attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +118,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
|
||||||
auto is_friend = false;
|
auto is_friend = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto builder = make_class_builder(cur);
|
auto builder = make_class_builder(context, cur);
|
||||||
type_safe::optional<cpp_entity_ref> semantic_parent;
|
type_safe::optional<cpp_entity_ref> semantic_parent;
|
||||||
if (!is_friend)
|
if (!is_friend)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,16 @@ namespace
|
||||||
};
|
};
|
||||||
|
|
||||||
bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
|
bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
|
||||||
const CXSourceLocation& loc, const char* token_str)
|
const CXSourceLocation& loc, const char* token_str, int inc = 1)
|
||||||
{
|
{
|
||||||
auto loc_after = get_next_location(tu, file, loc);
|
auto loc_after = get_next_location(tu, file, loc, inc);
|
||||||
|
if (!clang_Location_isFromMainFile(loc_after))
|
||||||
|
return false;
|
||||||
|
|
||||||
simple_tokenizer tokenizer(tu, clang_getRange(loc, loc_after), cur);
|
simple_tokenizer tokenizer(tu,
|
||||||
|
inc > 0 ? clang_getRange(loc, loc_after) :
|
||||||
|
clang_getRange(loc_after, loc),
|
||||||
|
cur);
|
||||||
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
|
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
|
||||||
return spelling == token_str;
|
return spelling == token_str;
|
||||||
}
|
}
|
||||||
|
|
@ -96,10 +101,27 @@ namespace
|
||||||
auto end = clang_getRangeEnd(extent);
|
auto end = clang_getRangeEnd(extent);
|
||||||
|
|
||||||
auto kind = clang_getCursorKind(cur);
|
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)
|
||||||
|
&& token_after_is(tu, file, cur, begin, "]", -2)
|
||||||
|
&& token_after_is(tu, file, cur, begin, "]", -3))
|
||||||
|
{
|
||||||
|
while (!token_after_is(tu, file, cur, begin, "[", -1)
|
||||||
|
&& !token_after_is(tu, file, cur, 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, cur, begin, "[")
|
||||||
|
&& token_after_is(tu, file, cur, get_next_location(tu, file, begin),
|
||||||
|
"["),
|
||||||
|
detail::parse_error_handler{}, cur,
|
||||||
|
"error in pre-function attribute parsing");
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
|
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
|
||||||
{
|
{
|
||||||
auto is_definition = false;
|
auto is_definition = false;
|
||||||
|
|
||||||
// if a function we need to remove the body
|
// if a function we need to remove the body
|
||||||
// it does not need to be parsed
|
// it does not need to be parsed
|
||||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||||
|
|
@ -216,6 +238,13 @@ namespace
|
||||||
while (!token_after_is(tu, file, cur, end, ";"))
|
while (!token_after_is(tu, file, cur, end, ";"))
|
||||||
end = get_next_location(tu, file, end);
|
end = get_next_location(tu, file, end);
|
||||||
}
|
}
|
||||||
|
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, cur, end, ",")
|
||||||
|
&& !token_after_is(tu, file, cur, end, ";"))
|
||||||
|
{
|
||||||
|
while (!token_after_is(tu, file, cur, end, ",")
|
||||||
|
&& !token_after_is(tu, file, cur, end, ";"))
|
||||||
|
end = get_next_location(tu, file, end);
|
||||||
|
}
|
||||||
else if (kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|
else if (kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|
||||||
|| kind == CXCursor_NonTypeTemplateParameter
|
|| kind == CXCursor_NonTypeTemplateParameter
|
||||||
|| kind == CXCursor_TemplateTemplateParameter
|
|| kind == CXCursor_TemplateTemplateParameter
|
||||||
|
|
@ -368,14 +397,103 @@ void detail::skip_brackets(detail::cxtoken_stream& stream)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool skip_attribute_impl(detail::cxtoken_stream& stream)
|
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
|
||||||
|
{
|
||||||
|
// 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, ":");
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return type_safe::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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, 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, 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() == "(")
|
||||||
|
{
|
||||||
|
auto end = find_closing_bracket(stream);
|
||||||
|
skip(stream, "(");
|
||||||
|
|
||||||
|
arguments = detail::to_string(stream, end);
|
||||||
|
|
||||||
|
stream.set_cur(end);
|
||||||
|
skip(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
|
// C++11 attribute
|
||||||
// [[<attribute>]]
|
// [[<attribute>]]
|
||||||
// ^
|
// ^
|
||||||
skip_brackets(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, ",");
|
||||||
|
}
|
||||||
|
|
||||||
// [[<attribute>]]
|
// [[<attribute>]]
|
||||||
// ^
|
// ^
|
||||||
skip(stream, "]");
|
skip(stream, "]");
|
||||||
|
|
@ -402,12 +520,13 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool detail::skip_attribute(detail::cxtoken_stream& stream)
|
cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream)
|
||||||
{
|
{
|
||||||
auto any = false;
|
cpp_attribute_list result;
|
||||||
while (skip_attribute_impl(stream))
|
while (parse_attribute_impl(result, stream))
|
||||||
any = true;
|
{
|
||||||
return any;
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <cppast/cpp_attribute.hpp>
|
||||||
#include <cppast/cpp_token.hpp>
|
#include <cppast/cpp_token.hpp>
|
||||||
|
|
||||||
#include "raii_wrapper.hpp"
|
#include "raii_wrapper.hpp"
|
||||||
|
|
@ -189,8 +190,8 @@ namespace cppast
|
||||||
// note: < might not work in the arguments of a template specialization
|
// note: < might not work in the arguments of a template specialization
|
||||||
void skip_brackets(cxtoken_stream& stream);
|
void skip_brackets(cxtoken_stream& stream);
|
||||||
|
|
||||||
// skips an attribute
|
// parses attributes
|
||||||
bool skip_attribute(cxtoken_stream& stream);
|
cpp_attribute_list parse_attributes(cxtoken_stream& stream);
|
||||||
|
|
||||||
// converts a token range to a string
|
// converts a token range to a string
|
||||||
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
|
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ namespace
|
||||||
|
|
||||||
// <identifier> [<attribute>],
|
// <identifier> [<attribute>],
|
||||||
// or: <identifier> [<attribute>] = <expression>,
|
// or: <identifier> [<attribute>] = <expression>,
|
||||||
auto& name = stream.get().value();
|
auto& name = stream.get().value();
|
||||||
detail::skip_attribute(stream);
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
|
||||||
std::unique_ptr<cpp_expression> value;
|
std::unique_ptr<cpp_expression> value;
|
||||||
if (detail::skip_if(stream, "="))
|
if (detail::skip_if(stream, "="))
|
||||||
|
|
@ -40,8 +40,10 @@ namespace
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
|
auto result = cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
|
||||||
std::move(value));
|
std::move(value));
|
||||||
|
result->add_attribute(attributes);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur,
|
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur,
|
||||||
|
|
@ -51,11 +53,11 @@ namespace
|
||||||
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
detail::cxtoken_stream stream(tokenizer, cur);
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
|
|
||||||
// [<attribute>] enum [class] [<attribute>] name [: type] {
|
// enum [class/struct] [<attribute>] name [: type] {
|
||||||
detail::skip_attribute(stream);
|
|
||||||
detail::skip(stream, "enum");
|
detail::skip(stream, "enum");
|
||||||
auto scoped = detail::skip_if(stream, "class");
|
auto scoped = detail::skip_if(stream, "class") || detail::skip_if(stream, "struct");
|
||||||
detail::skip_attribute(stream);
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
|
||||||
std::string scope;
|
std::string scope;
|
||||||
while (!detail::skip_if(stream, name.c_str()))
|
while (!detail::skip_if(stream, name.c_str()))
|
||||||
if (!detail::append_scope(stream, scope))
|
if (!detail::append_scope(stream, scope))
|
||||||
|
|
@ -70,7 +72,9 @@ namespace
|
||||||
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
|
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
|
||||||
auto type_given = detail::skip_if(stream, ":");
|
auto type_given = detail::skip_if(stream, ":");
|
||||||
|
|
||||||
return cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
|
auto result = cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
|
||||||
|
result.get().add_attribute(attributes);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,18 @@ namespace
|
||||||
auto name = detail::get_cursor_name(cur);
|
auto name = detail::get_cursor_name(cur);
|
||||||
auto type = detail::parse_type(context, cur, clang_getCursorType(cur));
|
auto type = detail::parse_type(context, cur, clang_getCursorType(cur));
|
||||||
|
|
||||||
auto default_value = detail::parse_default_value(context, cur, name.c_str());
|
cpp_attribute_list attributes;
|
||||||
|
auto default_value = detail::parse_default_value(attributes, context, cur, name.c_str());
|
||||||
|
|
||||||
|
std::unique_ptr<cpp_function_parameter> result;
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
return cpp_function_parameter::build(std::move(type), std::move(default_value));
|
result = cpp_function_parameter::build(std::move(type), std::move(default_value));
|
||||||
else
|
else
|
||||||
return cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur),
|
result = cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur),
|
||||||
name.c_str(), std::move(type),
|
name.c_str(), std::move(type),
|
||||||
std::move(default_value));
|
std::move(default_value));
|
||||||
|
result->add_attribute(attributes);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Builder>
|
template <class Builder>
|
||||||
|
|
@ -212,10 +216,11 @@ namespace
|
||||||
// just the tokens occurring in the prefix
|
// just the tokens occurring in the prefix
|
||||||
struct prefix_info
|
struct prefix_info
|
||||||
{
|
{
|
||||||
bool is_constexpr = false;
|
cpp_attribute_list attributes;
|
||||||
bool is_virtual = false;
|
bool is_constexpr = false;
|
||||||
bool is_explicit = false;
|
bool is_virtual = false;
|
||||||
bool is_friend = false;
|
bool is_explicit = false;
|
||||||
|
bool is_friend = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool prefix_end(detail::cxtoken_stream& stream, const char* name, bool is_ctor)
|
bool prefix_end(detail::cxtoken_stream& stream, const char* name, bool is_ctor)
|
||||||
|
|
@ -275,7 +280,14 @@ namespace
|
||||||
else if (detail::skip_if(stream, "explicit"))
|
else if (detail::skip_if(stream, "explicit"))
|
||||||
result.is_explicit = true;
|
result.is_explicit = true;
|
||||||
else
|
else
|
||||||
stream.bump();
|
{
|
||||||
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
if (attributes.empty())
|
||||||
|
stream.bump();
|
||||||
|
else
|
||||||
|
result.attributes.insert(result.attributes.end(), attributes.begin(),
|
||||||
|
attributes.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DEBUG_ASSERT(!stream.done(), detail::parse_error_handler{}, stream.cursor(),
|
DEBUG_ASSERT(!stream.done(), detail::parse_error_handler{}, stream.cursor(),
|
||||||
"unable to find end of function prefix");
|
"unable to find end of function prefix");
|
||||||
|
|
@ -283,12 +295,17 @@ namespace
|
||||||
{ // function name can be enclosed in parentheses
|
{ // function name can be enclosed in parentheses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
if (!attributes.empty())
|
||||||
|
result.attributes.insert(result.attributes.end(), attributes.begin(), attributes.end());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just the tokens occurring in the suffix
|
// just the tokens occurring in the suffix
|
||||||
struct suffix_info
|
struct suffix_info
|
||||||
{
|
{
|
||||||
|
cpp_attribute_list attributes;
|
||||||
std::unique_ptr<cpp_expression> noexcept_condition;
|
std::unique_ptr<cpp_expression> noexcept_condition;
|
||||||
cpp_function_body_kind body_kind;
|
cpp_function_body_kind body_kind;
|
||||||
cpp_cv cv_qualifier = cpp_cv_none;
|
cpp_cv cv_qualifier = cpp_cv_none;
|
||||||
|
|
@ -392,12 +409,13 @@ namespace
|
||||||
suffix_info result(stream.cursor());
|
suffix_info result(stream.cursor());
|
||||||
|
|
||||||
// syntax: <attribute> <cv> <ref> <exception>
|
// syntax: <attribute> <cv> <ref> <exception>
|
||||||
detail::skip_attribute(stream);
|
result.attributes = detail::parse_attributes(stream);
|
||||||
if (allow_qualifier)
|
if (allow_qualifier)
|
||||||
{
|
{
|
||||||
result.cv_qualifier = parse_cv(stream);
|
result.cv_qualifier = parse_cv(stream);
|
||||||
result.ref_qualifier = parse_ref(stream);
|
result.ref_qualifier = parse_ref(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detail::skip_if(stream, "throw"))
|
if (detail::skip_if(stream, "throw"))
|
||||||
// just because I can
|
// just because I can
|
||||||
detail::skip_brackets(stream);
|
detail::skip_brackets(stream);
|
||||||
|
|
@ -414,14 +432,17 @@ namespace
|
||||||
// check for trailing return type
|
// check for trailing return type
|
||||||
if (detail::skip_if(stream, "->"))
|
if (detail::skip_if(stream, "->"))
|
||||||
{
|
{
|
||||||
//detail::print_tokens(context.tu, context.file, stream.cursor());
|
|
||||||
// this is rather tricky to skip
|
// this is rather tricky to skip
|
||||||
// so loop over all tokens and see if matching keytokens occur
|
// so loop over all tokens and see if matching keytokens occur
|
||||||
// note that this isn't quite correct
|
// note that this isn't quite correct
|
||||||
// use a heuristic to skip brackets, which should be good enough
|
// use a heuristic to skip brackets, which should be good enough
|
||||||
while (!stream.done())
|
while (!stream.done())
|
||||||
{
|
{
|
||||||
if (stream.peek() == "(" || stream.peek() == "[" || stream.peek() == "<")
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
if (!attributes.empty())
|
||||||
|
result.attributes.insert(result.attributes.end(), attributes.begin(),
|
||||||
|
attributes.end());
|
||||||
|
else if (stream.peek() == "(" || stream.peek() == "[" || stream.peek() == "<")
|
||||||
detail::skip_brackets(stream);
|
detail::skip_brackets(stream);
|
||||||
else if (stream.peek() == "{")
|
else if (stream.peek() == "{")
|
||||||
// begin of definition
|
// begin of definition
|
||||||
|
|
@ -472,6 +493,11 @@ namespace
|
||||||
result.virtual_keywords.value() |= cpp_virtual_flags::override;
|
result.virtual_keywords.value() |= cpp_virtual_flags::override;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto attributes = detail::parse_attributes(stream);
|
||||||
|
if (!attributes.empty())
|
||||||
|
result.attributes.insert(result.attributes.end(), attributes.begin(),
|
||||||
|
attributes.end());
|
||||||
|
|
||||||
if (detail::skip_if(stream, "="))
|
if (detail::skip_if(stream, "="))
|
||||||
parse_body(stream, result, allow_virtual);
|
parse_body(stream, result, allow_virtual);
|
||||||
else if (detail::skip_if(stream, "{") || detail::skip_if(stream, ":")
|
else if (detail::skip_if(stream, "{") || detail::skip_if(stream, ":")
|
||||||
|
|
@ -499,6 +525,7 @@ namespace
|
||||||
detail::parse_type(context, cur,
|
detail::parse_type(context, cur,
|
||||||
clang_getCursorResultType(cur)));
|
clang_getCursorResultType(cur)));
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
|
builder.get().add_attribute(prefix.attributes);
|
||||||
|
|
||||||
add_parameters(context, builder, cur);
|
add_parameters(context, builder, cur);
|
||||||
if (clang_Cursor_isVariadic(cur))
|
if (clang_Cursor_isVariadic(cur))
|
||||||
|
|
@ -512,6 +539,7 @@ namespace
|
||||||
skip_parameters(stream);
|
skip_parameters(stream);
|
||||||
|
|
||||||
auto suffix = parse_suffix_info(stream, context, false, false);
|
auto suffix = parse_suffix_info(stream, context, false, false);
|
||||||
|
builder.get().add_attribute(suffix.attributes);
|
||||||
if (suffix.noexcept_condition)
|
if (suffix.noexcept_condition)
|
||||||
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||||
|
|
||||||
|
|
@ -617,6 +645,7 @@ namespace
|
||||||
auto allow_qualifiers = set_qualifier(0, builder, cpp_cv_none, cpp_ref_none);
|
auto allow_qualifiers = set_qualifier(0, builder, cpp_cv_none, cpp_ref_none);
|
||||||
|
|
||||||
auto suffix = parse_suffix_info(stream, context, allow_qualifiers, true);
|
auto suffix = parse_suffix_info(stream, context, allow_qualifiers, true);
|
||||||
|
builder.get().add_attribute(suffix.attributes);
|
||||||
set_qualifier(0, builder, suffix.cv_qualifier, suffix.ref_qualifier);
|
set_qualifier(0, builder, suffix.cv_qualifier, suffix.ref_qualifier);
|
||||||
if (suffix.noexcept_condition)
|
if (suffix.noexcept_condition)
|
||||||
builder.noexcept_condition(move(suffix.noexcept_condition));
|
builder.noexcept_condition(move(suffix.noexcept_condition));
|
||||||
|
|
@ -651,6 +680,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
|
||||||
detail::parse_type(context, cur,
|
detail::parse_type(context, cur,
|
||||||
clang_getCursorResultType(cur)));
|
clang_getCursorResultType(cur)));
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
|
builder.get().add_attribute(prefix.attributes);
|
||||||
add_parameters(context, builder, cur);
|
add_parameters(context, builder, cur);
|
||||||
if (clang_Cursor_isVariadic(cur))
|
if (clang_Cursor_isVariadic(cur))
|
||||||
builder.is_variadic();
|
builder.is_variadic();
|
||||||
|
|
@ -674,6 +704,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
|
||||||
detail::cxtoken_stream stream(tokenizer, cur);
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
|
|
||||||
auto prefix = parse_prefix_info(stream, "operator", false);
|
auto prefix = parse_prefix_info(stream, "operator", false);
|
||||||
|
|
||||||
// heuristic to find arguments tokens
|
// heuristic to find arguments tokens
|
||||||
// skip forward, skipping inside brackets
|
// skip forward, skipping inside brackets
|
||||||
auto type_start = stream.cur();
|
auto type_start = stream.cur();
|
||||||
|
|
@ -715,6 +746,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
|
||||||
cpp_conversion_op::builder builder("operator " + type_spelling,
|
cpp_conversion_op::builder builder("operator " + type_spelling,
|
||||||
detail::parse_type(context, cur, type));
|
detail::parse_type(context, cur, type));
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
|
builder.get().add_attribute(prefix.attributes);
|
||||||
if (prefix.is_explicit)
|
if (prefix.is_explicit)
|
||||||
builder.is_explicit();
|
builder.is_explicit();
|
||||||
else if (prefix.is_constexpr)
|
else if (prefix.is_constexpr)
|
||||||
|
|
@ -745,6 +777,8 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
|
||||||
cpp_constructor::builder builder(name.c_str());
|
cpp_constructor::builder builder(name.c_str());
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
add_parameters(context, builder, cur);
|
add_parameters(context, builder, cur);
|
||||||
|
builder.get().add_attribute(prefix.attributes);
|
||||||
|
|
||||||
if (clang_Cursor_isVariadic(cur))
|
if (clang_Cursor_isVariadic(cur))
|
||||||
builder.is_variadic();
|
builder.is_variadic();
|
||||||
if (prefix.is_constexpr)
|
if (prefix.is_constexpr)
|
||||||
|
|
@ -755,6 +789,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
|
||||||
skip_parameters(stream);
|
skip_parameters(stream);
|
||||||
|
|
||||||
auto suffix = parse_suffix_info(stream, context, false, false);
|
auto suffix = parse_suffix_info(stream, context, false, false);
|
||||||
|
builder.get().add_attribute(suffix.attributes);
|
||||||
if (suffix.noexcept_condition)
|
if (suffix.noexcept_condition)
|
||||||
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
builder.noexcept_condition(std::move(suffix.noexcept_condition));
|
||||||
|
|
||||||
|
|
@ -780,6 +815,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_destructor(const detail::parse_con
|
||||||
auto name = std::string("~") + stream.get().c_str();
|
auto name = std::string("~") + stream.get().c_str();
|
||||||
cpp_destructor::builder builder(std::move(name));
|
cpp_destructor::builder builder(std::move(name));
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
|
builder.get().add_attribute(prefix_info.attributes);
|
||||||
|
|
||||||
detail::skip(stream, "(");
|
detail::skip(stream, "(");
|
||||||
detail::skip(stream, ")");
|
detail::skip(stream, ")");
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace
|
||||||
is_inline = true;
|
is_inline = true;
|
||||||
|
|
||||||
skip(stream, "namespace");
|
skip(stream, "namespace");
|
||||||
skip_attribute(stream);
|
auto attributes = parse_attributes(stream);
|
||||||
|
|
||||||
// <identifier> {
|
// <identifier> {
|
||||||
// or when anonymous: {
|
// or when anonymous: {
|
||||||
|
|
@ -33,9 +33,15 @@ namespace
|
||||||
return cpp_namespace::builder("", is_inline);
|
return cpp_namespace::builder("", is_inline);
|
||||||
|
|
||||||
auto& name = stream.get().value();
|
auto& name = stream.get().value();
|
||||||
skip_attribute(stream);
|
|
||||||
|
auto other_attributes = parse_attributes(stream);
|
||||||
|
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
|
||||||
|
|
||||||
skip(stream, "{");
|
skip(stream, "{");
|
||||||
return cpp_namespace::builder(name.c_str(), is_inline);
|
|
||||||
|
auto result = cpp_namespace::builder(name.c_str(), is_inline);
|
||||||
|
result.get().add_attribute(attributes);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@ namespace cppast
|
||||||
};
|
};
|
||||||
|
|
||||||
// parse default value of variable, function parameter...
|
// parse default value of variable, function parameter...
|
||||||
std::unique_ptr<cpp_expression> parse_default_value(const parse_context& context,
|
std::unique_ptr<cpp_expression> parse_default_value(cpp_attribute_list& attributes,
|
||||||
|
const parse_context& context,
|
||||||
const CXCursor& cur, const char* name);
|
const CXCursor& cur, const char* name);
|
||||||
|
|
||||||
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
|
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,9 @@ namespace
|
||||||
|
|
||||||
auto name = detail::get_cursor_name(cur);
|
auto name = detail::get_cursor_name(cur);
|
||||||
auto type = clang_getCursorType(cur);
|
auto type = clang_getCursorType(cur);
|
||||||
auto def = detail::parse_default_value(context, cur, name.c_str());
|
|
||||||
|
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::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
detail::cxtoken_stream stream(tokenizer, cur);
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
|
|
@ -108,10 +110,13 @@ namespace
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
|
auto result =
|
||||||
name.c_str(),
|
cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
|
||||||
detail::parse_type(context, cur, type),
|
name.c_str(),
|
||||||
is_variadic, std::move(def));
|
detail::parse_type(context, cur, type),
|
||||||
|
is_variadic, std::move(def));
|
||||||
|
result->add_attribute(attributes);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
|
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
|
||||||
|
|
@ -196,6 +201,17 @@ namespace
|
||||||
builder.add_parameter(parse_template_parameter(context, child));
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context,
|
std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context,
|
||||||
|
|
@ -210,6 +226,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse
|
||||||
if (!builder)
|
if (!builder)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
context.comments.match(builder.value().get(), cur);
|
context.comments.match(builder.value().get(), cur);
|
||||||
|
builder.value().get().add_attribute(builder.value().get().begin()->attributes());
|
||||||
parse_parameters(builder.value(), context, cur);
|
parse_parameters(builder.value(), context, cur);
|
||||||
return builder.value().finish(*context.idx, detail::get_entity_id(cur),
|
return builder.value().finish(*context.idx, detail::get_entity_id(cur),
|
||||||
false); // not a definition
|
false); // not a definition
|
||||||
|
|
@ -245,15 +262,12 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
|
||||||
|
|
||||||
if (!func)
|
if (!func)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
auto func_ptr = func.get();
|
||||||
// steal comment
|
|
||||||
auto comment = type_safe::copy(func->comment());
|
|
||||||
func->set_comment(type_safe::nullopt);
|
|
||||||
|
|
||||||
cpp_function_template::builder builder(
|
cpp_function_template::builder builder(
|
||||||
std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())));
|
std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())));
|
||||||
builder.get().set_comment(std::move(comment));
|
|
||||||
parse_parameters(builder, context, cur);
|
parse_parameters(builder, context, cur);
|
||||||
|
handle_comment_attributes(builder.get(), *func_ptr);
|
||||||
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||||
builder.get().function().is_definition());
|
builder.get().function().is_definition());
|
||||||
}
|
}
|
||||||
|
|
@ -315,16 +329,13 @@ std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specializati
|
||||||
}
|
}
|
||||||
if (!func)
|
if (!func)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
auto func_ptr = func.get();
|
||||||
// steal comment
|
|
||||||
auto comment = type_safe::copy(func->comment());
|
|
||||||
func->set_comment(type_safe::nullopt);
|
|
||||||
|
|
||||||
cpp_function_template_specialization::builder
|
cpp_function_template_specialization::builder
|
||||||
builder(std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())),
|
builder(std::unique_ptr<cpp_function_base>(static_cast<cpp_function_base*>(func.release())),
|
||||||
cpp_template_ref(detail::get_entity_id(templ), ""));
|
cpp_template_ref(detail::get_entity_id(templ), ""));
|
||||||
builder.get().set_comment(std::move(comment));
|
|
||||||
parse_arguments(builder, context, cur);
|
parse_arguments(builder, context, cur);
|
||||||
|
handle_comment_attributes(builder.get(), *func_ptr);
|
||||||
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||||
builder.get().function().is_definition());
|
builder.get().function().is_definition());
|
||||||
}
|
}
|
||||||
|
|
@ -337,14 +348,11 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class_template(const detail::parse
|
||||||
auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor());
|
auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor());
|
||||||
if (!c)
|
if (!c)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
auto c_ptr = c.get();
|
||||||
// steal comment
|
|
||||||
auto comment = type_safe::copy(c->comment());
|
|
||||||
c->set_comment(type_safe::nullopt);
|
|
||||||
|
|
||||||
cpp_class_template::builder builder(
|
cpp_class_template::builder builder(
|
||||||
std::unique_ptr<cpp_class>(static_cast<cpp_class*>(c.release())));
|
std::unique_ptr<cpp_class>(static_cast<cpp_class*>(c.release())));
|
||||||
builder.get().set_comment(std::move(comment));
|
handle_comment_attributes(builder.get(), *c_ptr);
|
||||||
parse_parameters(builder, context, cur);
|
parse_parameters(builder, context, cur);
|
||||||
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||||
builder.get().class_().is_definition());
|
builder.get().class_().is_definition());
|
||||||
|
|
@ -377,15 +385,12 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class_template_specialization(
|
||||||
auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor());
|
auto c = detail::parse_cpp_class(context, cur, clang_getNullCursor());
|
||||||
if (!c)
|
if (!c)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
auto c_ptr = c.get();
|
||||||
// steal comment
|
|
||||||
auto comment = type_safe::copy(c->comment());
|
|
||||||
c->set_comment(type_safe::nullopt);
|
|
||||||
|
|
||||||
cpp_class_template_specialization::builder
|
cpp_class_template_specialization::builder
|
||||||
builder(std::unique_ptr<cpp_class>(static_cast<cpp_class*>(c.release())),
|
builder(std::unique_ptr<cpp_class>(static_cast<cpp_class*>(c.release())),
|
||||||
cpp_template_ref(detail::get_entity_id(primary), ""));
|
cpp_template_ref(detail::get_entity_id(primary), ""));
|
||||||
builder.get().set_comment(std::move(comment));
|
handle_comment_attributes(builder.get(), *c_ptr);
|
||||||
parse_parameters(builder, context, cur);
|
parse_parameters(builder, context, cur);
|
||||||
parse_arguments(builder, context, cur);
|
parse_arguments(builder, context, cur);
|
||||||
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||||
|
|
|
||||||
|
|
@ -749,13 +749,25 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_type_alias(const detail::parse_con
|
||||||
auto type = parse_type(context, clang_Cursor_isNull(template_cur) ? cur : template_cur,
|
auto type = parse_type(context, clang_Cursor_isNull(template_cur) ? cur : template_cur,
|
||||||
clang_getTypedefDeclUnderlyingType(cur));
|
clang_getTypedefDeclUnderlyingType(cur));
|
||||||
|
|
||||||
|
std::unique_ptr<cpp_type_alias> result;
|
||||||
if (!clang_Cursor_isNull(template_cur))
|
if (!clang_Cursor_isNull(template_cur))
|
||||||
return cpp_type_alias::build(name.c_str(), std::move(type));
|
result = cpp_type_alias::build(name.c_str(), std::move(type));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto result =
|
result =
|
||||||
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
|
cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
|
||||||
context.comments.match(*result, cur);
|
context.comments.match(*result, cur);
|
||||||
return std::move(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// look for attributes
|
||||||
|
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
|
if (detail::skip_if(stream, "using"))
|
||||||
|
{
|
||||||
|
// syntax: using <identifier> attributes
|
||||||
|
detail::skip(stream, name.c_str());
|
||||||
|
result->add_attribute(detail::parse_attributes(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@
|
||||||
|
|
||||||
using namespace cppast;
|
using namespace cppast;
|
||||||
|
|
||||||
std::unique_ptr<cpp_expression> detail::parse_default_value(const detail::parse_context& context,
|
std::unique_ptr<cpp_expression> detail::parse_default_value(cpp_attribute_list& attributes,
|
||||||
|
const detail::parse_context& context,
|
||||||
const CXCursor& cur, const char* name)
|
const CXCursor& cur, const char* name)
|
||||||
{
|
{
|
||||||
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
|
|
@ -36,7 +37,13 @@ std::unique_ptr<cpp_expression> detail::parse_default_value(const detail::parse_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
stream.bump();
|
{
|
||||||
|
auto cur_attributes = detail::parse_attributes(stream);
|
||||||
|
if (cur_attributes.empty())
|
||||||
|
stream.bump();
|
||||||
|
else
|
||||||
|
attributes.insert(attributes.end(), cur_attributes.begin(), cur_attributes.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (has_default)
|
if (has_default)
|
||||||
return parse_raw_expression(context, stream, stream.end(),
|
return parse_raw_expression(context, stream, stream.end(),
|
||||||
|
|
@ -65,10 +72,12 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
|
||||||
else if (token.value() == "constexpr")
|
else if (token.value() == "constexpr")
|
||||||
is_constexpr = true;
|
is_constexpr = true;
|
||||||
|
|
||||||
|
cpp_attribute_list attributes;
|
||||||
|
auto default_value = parse_default_value(attributes, context, cur, name.c_str());
|
||||||
|
|
||||||
std::unique_ptr<cpp_variable> result;
|
std::unique_ptr<cpp_variable> result;
|
||||||
if (clang_isCursorDefinition(cur))
|
if (clang_isCursorDefinition(cur))
|
||||||
{
|
{
|
||||||
auto default_value = parse_default_value(context, cur, name.c_str());
|
|
||||||
result =
|
result =
|
||||||
cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
|
cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
|
||||||
std::move(default_value), storage_class, is_constexpr);
|
std::move(default_value), storage_class, is_constexpr);
|
||||||
|
|
@ -77,6 +86,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
|
||||||
result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type),
|
result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type),
|
||||||
storage_class, is_constexpr);
|
storage_class, is_constexpr);
|
||||||
context.comments.match(*result, cur);
|
context.comments.match(*result, cur);
|
||||||
|
result->add_attribute(attributes);
|
||||||
return std::move(result);
|
return std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +99,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_variable(const detail::pars
|
||||||
auto type = parse_type(context, cur, clang_getCursorType(cur));
|
auto type = parse_type(context, cur, clang_getCursorType(cur));
|
||||||
auto is_mutable = clang_CXXField_isMutable(cur) != 0u;
|
auto is_mutable = clang_CXXField_isMutable(cur) != 0u;
|
||||||
|
|
||||||
|
cpp_attribute_list attributes;
|
||||||
|
auto default_value = parse_default_value(attributes, context, cur, name.c_str());
|
||||||
|
|
||||||
std::unique_ptr<cpp_member_variable_base> result;
|
std::unique_ptr<cpp_member_variable_base> result;
|
||||||
if (clang_Cursor_isBitField(cur))
|
if (clang_Cursor_isBitField(cur))
|
||||||
{
|
{
|
||||||
|
|
@ -102,10 +115,10 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_variable(const detail::pars
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto default_value = parse_default_value(context, cur, name.c_str());
|
|
||||||
result = cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(),
|
result = cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(),
|
||||||
std::move(type), std::move(default_value), is_mutable);
|
std::move(type), std::move(default_value), is_mutable);
|
||||||
}
|
}
|
||||||
|
result->add_attribute(attributes);
|
||||||
context.comments.match(*result, cur);
|
context.comments.match(*result, cur);
|
||||||
return std::move(result);
|
return std::move(result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ endif()
|
||||||
set(tests
|
set(tests
|
||||||
code_generator.cpp
|
code_generator.cpp
|
||||||
cpp_alias_template.cpp
|
cpp_alias_template.cpp
|
||||||
|
cpp_attribute.cpp
|
||||||
cpp_class.cpp
|
cpp_class.cpp
|
||||||
cpp_class_template.cpp
|
cpp_class_template.cpp
|
||||||
cpp_enum.cpp
|
cpp_enum.cpp
|
||||||
|
|
|
||||||
198
test/cpp_attribute.cpp
Normal file
198
test/cpp_attribute.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||||
|
// This file is subject to the license terms in the LICENSE file
|
||||||
|
// found in the top-level directory of this distribution.
|
||||||
|
|
||||||
|
#include <cppast/cpp_attribute.hpp>
|
||||||
|
|
||||||
|
#include <cppast/cpp_function.hpp>
|
||||||
|
|
||||||
|
#include "test_parser.hpp"
|
||||||
|
|
||||||
|
using namespace cppast;
|
||||||
|
|
||||||
|
TEST_CASE("cpp_attribute")
|
||||||
|
{
|
||||||
|
auto code = R"(
|
||||||
|
// multiple attributes
|
||||||
|
[[attribute1]] void [[attribute2]] a();
|
||||||
|
[[attribute1, attribute2]] void b();
|
||||||
|
|
||||||
|
// variadic attributes - not actually supported by clang
|
||||||
|
//[[variadic...]] void c();
|
||||||
|
|
||||||
|
// scoped attributes
|
||||||
|
[[ns::attribute]] void d();
|
||||||
|
|
||||||
|
// argument attributes
|
||||||
|
[[attribute(arg1, arg2, +(){}, 42, "Hello!")]] void e();
|
||||||
|
|
||||||
|
// all of the above
|
||||||
|
[[ns::attribute(+, -, 0 4), other_attribute]] void f();
|
||||||
|
|
||||||
|
// known attributes
|
||||||
|
[[deprecated]] void g();
|
||||||
|
[[maybe_unused]] void h();
|
||||||
|
[[nodiscard]] int i();
|
||||||
|
[[noreturn]] void j();
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto file = parse({}, "cpp_attribute.cpp", code);
|
||||||
|
|
||||||
|
auto check_attribute = [](const cpp_attribute& attr, const char* name,
|
||||||
|
type_safe::optional<std::string> scope, bool variadic,
|
||||||
|
const char* args = "",
|
||||||
|
cpp_attribute_kind kind = cpp_attribute_kind::unknown) {
|
||||||
|
REQUIRE(attr.kind() == kind);
|
||||||
|
REQUIRE(attr.name() == name);
|
||||||
|
REQUIRE(attr.scope() == scope);
|
||||||
|
REQUIRE(attr.is_variadic() == variadic);
|
||||||
|
|
||||||
|
if (attr.arguments())
|
||||||
|
REQUIRE(attr.arguments().value().as_string() == args);
|
||||||
|
else
|
||||||
|
REQUIRE(*args == '\0');
|
||||||
|
};
|
||||||
|
|
||||||
|
auto count =
|
||||||
|
test_visit<cpp_function>(*file,
|
||||||
|
[&](const cpp_entity& e) {
|
||||||
|
auto& attributes = e.attributes();
|
||||||
|
REQUIRE(attributes.size() >= 1u);
|
||||||
|
auto& attr = attributes.front();
|
||||||
|
|
||||||
|
if (e.name() == "a" || e.name() == "b")
|
||||||
|
{
|
||||||
|
REQUIRE(attributes.size() == 2u);
|
||||||
|
check_attribute(attr, "attribute1", type_safe::nullopt,
|
||||||
|
false);
|
||||||
|
check_attribute(attributes[1u], "attribute2",
|
||||||
|
type_safe::nullopt, false);
|
||||||
|
}
|
||||||
|
else if (e.name() == "c")
|
||||||
|
check_attribute(attr, "variadic", type_safe::nullopt,
|
||||||
|
true);
|
||||||
|
else if (e.name() == "d")
|
||||||
|
check_attribute(attr, "attribute", "ns", false);
|
||||||
|
else if (e.name() == "e")
|
||||||
|
check_attribute(attr, "attribute", type_safe::nullopt,
|
||||||
|
false, R"(arg1,arg2,+(){},42,"Hello!")");
|
||||||
|
else if (e.name() == "f")
|
||||||
|
{
|
||||||
|
REQUIRE(attributes.size() == 2u);
|
||||||
|
check_attribute(attr, "attribute", "ns", false, "+,-,0 4");
|
||||||
|
check_attribute(attributes[1u], "other_attribute",
|
||||||
|
type_safe::nullopt, false);
|
||||||
|
}
|
||||||
|
else if (e.name() == "g")
|
||||||
|
check_attribute(attr, "deprecated", type_safe::nullopt,
|
||||||
|
false, "", cpp_attribute_kind::deprecated);
|
||||||
|
else if (e.name() == "h")
|
||||||
|
check_attribute(attr, "maybe_unused", type_safe::nullopt,
|
||||||
|
false, "",
|
||||||
|
cpp_attribute_kind::maybe_unused);
|
||||||
|
else if (e.name() == "i")
|
||||||
|
check_attribute(attr, "nodiscard", type_safe::nullopt,
|
||||||
|
false, "", cpp_attribute_kind::nodiscard);
|
||||||
|
else if (e.name() == "j")
|
||||||
|
check_attribute(attr, "noreturn", type_safe::nullopt,
|
||||||
|
false, "", cpp_attribute_kind::noreturn);
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
REQUIRE(count == 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("cpp_attribute matching")
|
||||||
|
{
|
||||||
|
auto code = R"(
|
||||||
|
// classes
|
||||||
|
struct [[a]] a {};
|
||||||
|
class [[b]] b {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class [[c]] c {};
|
||||||
|
template <typename T>
|
||||||
|
class [[c]] c<T*> {};
|
||||||
|
template <>
|
||||||
|
class [[c]] c<int> {};
|
||||||
|
|
||||||
|
// enums
|
||||||
|
enum [[e]] e {};
|
||||||
|
enum class [[f]] f
|
||||||
|
{
|
||||||
|
a [[a]],
|
||||||
|
b [[b]] = 42,
|
||||||
|
};
|
||||||
|
|
||||||
|
// functions
|
||||||
|
[[g]] void g();
|
||||||
|
void [[h]] h();
|
||||||
|
void i [[i]] ();
|
||||||
|
void j() [[j]];
|
||||||
|
auto k() -> int [[k]];
|
||||||
|
|
||||||
|
struct [[member_functions]] member_functions
|
||||||
|
{
|
||||||
|
void a() [[a]];
|
||||||
|
void b() const && [[b]];
|
||||||
|
virtual void c() [[c]] final;
|
||||||
|
virtual void d() [[d]] = 0;
|
||||||
|
|
||||||
|
[[member_functions]] member_functions();
|
||||||
|
member_functions(const member_functions&) [[member_functions]];
|
||||||
|
};
|
||||||
|
|
||||||
|
// variables
|
||||||
|
[[l]] const int l = 42;
|
||||||
|
static void* [[m]] m;
|
||||||
|
|
||||||
|
void [[function_params]] function_params
|
||||||
|
([[a]] int a, int [[b]] b, int c [[c]] = 42);
|
||||||
|
|
||||||
|
struct [[members]] members
|
||||||
|
{
|
||||||
|
int [[a]] a;
|
||||||
|
int [[b]] b : 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct [[bases]] bases
|
||||||
|
: [[a]] public a,
|
||||||
|
[[members]] members
|
||||||
|
{};
|
||||||
|
|
||||||
|
// namespace
|
||||||
|
namespace [[n]] n {}
|
||||||
|
|
||||||
|
// type aliases
|
||||||
|
using o [[o]] = int;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using p [[p]] = T;
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto file = parse({}, "cpp_attribute__matching.cpp", code);
|
||||||
|
|
||||||
|
auto count = 0u;
|
||||||
|
auto check = [&](const cppast::cpp_entity& e) {
|
||||||
|
INFO(e.name());
|
||||||
|
REQUIRE(e.attributes().size() == 1u);
|
||||||
|
REQUIRE(e.attributes().begin()->name() == e.name());
|
||||||
|
++count;
|
||||||
|
};
|
||||||
|
|
||||||
|
visit(*file, [&](const cppast::cpp_entity& e, const cppast::visitor_info& info) {
|
||||||
|
if (info.event != cppast::visitor_info::container_entity_exit
|
||||||
|
&& e.kind() != cppast::cpp_file::kind() && !is_friended(e) && !is_templated(e))
|
||||||
|
{
|
||||||
|
check(e);
|
||||||
|
if (e.kind() == cppast::cpp_function::kind())
|
||||||
|
for (auto& param : static_cast<const cppast::cpp_function&>(e).parameters())
|
||||||
|
check(param);
|
||||||
|
else if (e.kind() == cppast::cpp_class::kind())
|
||||||
|
for (auto& base : static_cast<const cppast::cpp_class&>(e).bases())
|
||||||
|
check(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
REQUIRE(count == 36u);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue