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

@ -73,11 +73,16 @@ namespace
};
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]));
return spelling == token_str;
}
@ -96,10 +101,27 @@ namespace
auto end = clang_getRangeEnd(extent);
auto kind = clang_getCursorKind(cur);
if ((cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur))
|| kind == CXCursor_VarDecl || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|| kind == CXCursor_NonTypeTemplateParameter)
&& 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)))
{
auto is_definition = false;
// if a function we need to remove the body
// it does not need to be parsed
detail::visit_children(cur, [&](const CXCursor& child) {
@ -216,6 +238,13 @@ namespace
while (!token_after_is(tu, file, cur, 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
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter
@ -368,14 +397,103 @@ void detail::skip_brackets(detail::cxtoken_stream& stream)
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() == "[")
{
// C++11 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>]]
// ^
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;
while (skip_attribute_impl(stream))
any = true;
return any;
cpp_attribute_list result;
while (parse_attribute_impl(result, stream))
{
}
return result;
}
namespace