Implement attribute parsing

This commit is contained in:
Jonathan Müller 2017-10-29 19:42:16 +01:00
commit ccb2aaa189
15 changed files with 512 additions and 86 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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