Add integration test that parses the standard library

This commit is contained in:
Jonathan Müller 2017-06-23 09:43:17 +02:00
commit 70ac5cf111
24 changed files with 650 additions and 321 deletions

View file

@ -59,4 +59,4 @@ script:
- mkdir build/ && cd build/
- $CMAKE -DCMAKE_CXX_FLAGS="-Werror -pedantic -Wall -Wextra -Wconversion -Wsign-conversion -Wno-parentheses" -DLLVM_DOWNLOAD_OS_NAME=$LLVM_DOWNLOAD_OS_NAME -DLLVM_PREFERRED_VERSION=$LLVM_VERSION ../
- $CMAKE --build .
- ./test/cppast_test
- if [[ "$LLVM_VERSION" == "4.0.0" ]]; then ./test/cppast_test \*; else ./test/cppast_test; fi

View file

@ -5,7 +5,7 @@ build_script:
- cmd: clang++ --version
- cmd: cmake -G"Visual Studio 14 2015 Win64" -DLLVM_VERSION_EXPLICIT=4.0.0 -DLIBCLANG_LIBRARY="C:/Program Files/LLVM/lib/libclang.lib" -DLIBCLANG_INCLUDE_DIR="C:/Program Files/LLVM/include" -DLIBCLANG_SYSTEM_INCLUDE_DIR="C:/"Program Files"/LLVM/lib/clang/4.0.0/include" -DCLANG_BINARY="C:/Program Files/LLVM/bin/clang++.exe" ../
- cmd: cmake --build . -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
- cmd: cmake --build . -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal
test_script:
- cmd: test\Debug\cppast_test.exe
- cmd: test\Debug\cppast_test.exe *

View file

@ -482,8 +482,9 @@ namespace cppast
/// \exclude
namespace detail
{
void write_template_arguments(code_generator::output& output,
type_safe::array_ref<const cpp_template_argument> arguments);
void write_template_arguments(
code_generator::output& output,
type_safe::optional<type_safe::array_ref<const cpp_template_argument>> arguments);
} // namespace detail
} // namespace cppast

View file

@ -80,9 +80,11 @@ namespace cppast
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](),
/// as no other [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_include_directive> build(const cpp_file_ref& target,
cpp_include_kind kind)
cpp_include_kind kind,
std::string full_path)
{
return std::unique_ptr<cpp_include_directive>(new cpp_include_directive(target, kind));
return std::unique_ptr<cpp_include_directive>(
new cpp_include_directive(target, kind, std::move(full_path)));
}
/// \returns A reference to the [cppast::cpp_file]() it includes.
@ -97,17 +99,28 @@ namespace cppast
return kind_;
}
/// \returns The full path of the included file.
const std::string& full_path() const noexcept
{
return full_path_;
}
private:
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind)
: cpp_entity(target.name()), target_(target.id()[0u]), kind_(kind)
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind,
std::string full_path)
: cpp_entity(target.name()),
target_(target.id()[0u]),
kind_(kind),
full_path_(std::move(full_path))
{
DEBUG_ASSERT(!target.is_overloaded(), detail::precondition_error_handler{});
}
cpp_entity_id target_;
cpp_include_kind kind_;
std::string full_path_;
};
} // namespace cppast

View file

@ -140,12 +140,15 @@ namespace cppast
type_safe::variant_type<std::vector<cpp_template_argument>>{});
}
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
/// \returns An array ref to the [cppast::cpp_template_argument](), if there are any.
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
type_safe::array_ref<const cpp_template_argument> arguments() const noexcept
type_safe::optional<type_safe::array_ref<const cpp_template_argument>> arguments() const
noexcept
{
auto& vec =
arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
if (vec.empty())
return type_safe::nullopt;
return type_safe::ref(vec.data(), vec.size());
}

View file

@ -577,8 +577,13 @@ namespace
output << keyword("virtual") << whitespace;
}
void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt)
void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt,
bool is_definition)
{
if (is_definition)
// don't include it in definition
return;
if (is_overriding(virt))
output << whitespace << keyword("override");
if (is_final(virt))
@ -667,7 +672,7 @@ namespace
detail::write_type(output, func.return_type(), "");
}
write_suffix_virtual(output, func.virtual_info());
write_suffix_virtual(output, func.virtual_info(), func.is_definition());
write_function_body(output, func, is_pure(func.virtual_info()));
}
return static_cast<bool>(output);
@ -701,7 +706,7 @@ namespace
write_noexcept(output, op,
need_ws || output.formatting().is_set(formatting_flags::operator_ws));
write_suffix_virtual(output, op.virtual_info());
write_suffix_virtual(output, op.virtual_info(), op.is_definition());
write_function_body(output, op, is_pure(op.virtual_info()));
}
return static_cast<bool>(output);
@ -740,7 +745,7 @@ namespace
<< punctuation("(") << punctuation(")");
write_noexcept(output, dtor, output.formatting().is_set(formatting_flags::operator_ws));
write_suffix_virtual(output, dtor.virtual_info());
write_suffix_virtual(output, dtor.virtual_info(), dtor.is_definition());
write_function_body(output, dtor, is_pure(dtor.virtual_info()));
}
return static_cast<bool>(output);
@ -1030,29 +1035,34 @@ bool cppast::generate_code(code_generator& generator, const cpp_entity& e)
return false;
}
void detail::write_template_arguments(code_generator::output& output,
type_safe::array_ref<const cpp_template_argument> arguments)
void detail::write_template_arguments(
code_generator::output& output,
type_safe::optional<type_safe::array_ref<const cpp_template_argument>> arguments)
{
if (arguments.size() == 0u)
return;
output << punctuation("<") << bracket_ws;
auto need_sep = false;
for (auto& arg : arguments)
if (!arguments)
{
if (need_sep)
output << comma;
else
need_sep = true;
if (auto type = arg.type())
detail::write_type(output, type.value(), "");
else if (auto expr = arg.expression())
detail::write_expression(output, expr.value());
else if (auto templ = arg.template_ref())
output << templ.value();
else
DEBUG_UNREACHABLE(detail::assert_handler{});
output << punctuation("<") << punctuation(">");
}
else
{
output << punctuation("<") << bracket_ws;
auto need_sep = false;
for (auto& arg : arguments.value())
{
if (need_sep)
output << comma;
else
need_sep = true;
if (auto type = arg.type())
detail::write_type(output, type.value(), "");
else if (auto expr = arg.expression())
detail::write_expression(output, expr.value());
else if (auto templ = arg.template_ref())
output << templ.value();
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
output << bracket_ws << punctuation(">");
}
output << bracket_ws << punctuation(">");
}

View file

@ -14,6 +14,9 @@ namespace
std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context,
const CXCursor& cur)
{
if (clang_isAttribute(clang_getCursorKind(cur)))
return nullptr;
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur,
"unexpected child cursor of enum");
@ -84,8 +87,10 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_enum(const detail::parse_context&
{
auto entity = parse_enum_value(context, child);
if (entity)
{
context.comments.match(*entity, child);
builder.add_value(std::move(entity));
builder.add_value(std::move(entity));
}
}
catch (parse_error& ex)
{

View file

@ -74,7 +74,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_friend(const detail::parse_context
}
else if (clang_isDeclaration(kind))
{
entity = parse_entity(context, child);
entity = parse_entity(context, child, cur);
if (entity)
{
// steal comment

View file

@ -84,13 +84,72 @@ namespace
detail::skip_brackets(stream);
}
bool is_class(const CXCursor& parent)
{
auto kind = clang_getCursorKind(parent);
return kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl
|| kind == CXCursor_UnionDecl || kind == CXCursor_ClassTemplate
|| kind == CXCursor_ClassTemplatePartialSpecialization;
}
CXCursor get_definition_scope(const CXCursor& cur, bool is_friend)
{
auto parent = clang_getCursorLexicalParent(cur);
if (is_friend)
{
// find the lexical parent that isn't a class
// as the definition scope is a namespace
while (is_class(parent))
parent = clang_getCursorSemanticParent(parent);
DEBUG_ASSERT(clang_getCursorKind(parent) == CXCursor_Namespace
|| clang_getCursorKind(parent) == CXCursor_TranslationUnit,
detail::parse_error_handler{}, cur,
"unable to find definition scope of friend");
}
return parent;
}
bool equivalent_cursor(const CXCursor& a, const CXCursor& b)
{
if (clang_getCursorKind(a) == clang_getCursorKind(b)
&& clang_getCursorKind(a) == CXCursor_Namespace)
return detail::cxstring(clang_getCursorUSR(a))
== detail::cxstring(clang_getCursorUSR(b));
else
return clang_equalCursors(a, b) == 1;
}
type_safe::optional<cpp_entity_ref> parse_scope(const CXCursor& cur, bool is_friend)
{
std::string scope;
// find the semantic parents we need until we're at the same level of the parent where the definition is
// the semantic parents are all the scopes that need to be appended
for (auto definition = get_definition_scope(cur, is_friend),
parent = clang_getCursorSemanticParent(cur);
!equivalent_cursor(definition, parent); parent = clang_getCursorSemanticParent(parent))
{
DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(parent)),
detail::parse_error_handler{}, cur,
"infinite loop while calculating scope");
auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent));
scope = parent_name.std_str() + "::" + std::move(scope);
}
if (scope.empty())
return type_safe::nullopt;
else
return cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
}
// just the tokens occurring in the prefix
struct prefix_info
{
type_safe::optional<cpp_entity_ref> semantic_parent;
bool is_constexpr = false;
bool is_virtual = false;
bool is_explicit = false;
bool is_constexpr = false;
bool is_virtual = false;
bool is_explicit = false;
bool is_friend = false;
};
bool prefix_end(detail::token_stream& stream, const char* name, bool is_ctor)
@ -99,34 +158,39 @@ namespace
// name can have multiple tokens if it is an operator
if (!detail::skip_if(stream, name, true))
return false;
else if (!is_ctor)
return true;
// if we reach this point, we've encountered the name of a constructor
// need to make sure it is not actually a class name
else if (stream.peek() == "::")
{
// after name came "::", it is a class name
stream.set_cur(cur);
else if (stream.peek() == "," || stream.peek() == ">" || stream.peek() == ">>")
// argument to template parameters
return false;
}
else if (stream.peek() == "<")
else if (is_ctor)
{
// after name came "<", it might be arguments for a class template,
// or just a specialization
// check if ( comes after the arguments
detail::skip_brackets(stream);
if (stream.peek() == "(")
// need to make sure it is not actually a class name
if (stream.peek() == "::")
{
// it was just a specialization, we're at the end
stream.set_cur(cur);
return true;
}
else
{
// class arguments
// after name came "::", it is a class name
stream.set_cur(cur);
return false;
}
else if (stream.peek() == "<")
{
// after name came "<", it might be arguments for a class template,
// or just a specialization
// check if ( comes after the arguments
detail::skip_brackets(stream);
if (stream.peek() == "(")
{
// it was just a specialization, we're at the end
stream.set_cur(cur);
return true;
}
else
{
// class arguments
stream.set_cur(cur);
return false;
}
}
else
return true;
}
else
return true;
@ -136,35 +200,21 @@ namespace
{
prefix_info result;
std::string scope;
while (!stream.done() && !prefix_end(stream, name, is_ctor))
{
if (detail::skip_if(stream, "constexpr"))
{
result.is_constexpr = true;
scope.clear();
}
else if (detail::skip_if(stream, "virtual"))
{
result.is_virtual = true;
scope.clear();
}
else if (detail::skip_if(stream, "explicit"))
{
result.is_explicit = true;
scope.clear();
}
else if (!detail::append_scope(stream, scope))
else
stream.bump();
}
DEBUG_ASSERT(!stream.done(), detail::parse_error_handler{}, stream.cursor(),
"unable to find end of function prefix");
if (!scope.empty() && scope.back() == ':')
{
result.semantic_parent =
cpp_entity_ref(detail::get_entity_id(
clang_getCursorSemanticParent(stream.cursor())),
std::move(scope));
while (detail::skip_if(stream, ")"))
{ // function name can be enclosed in parentheses
}
return result;
@ -367,7 +417,8 @@ namespace
}
std::unique_ptr<cpp_entity> parse_cpp_function_impl(const detail::parse_context& context,
const CXCursor& cur, bool is_static)
const CXCursor& cur, bool is_static,
bool is_friend)
{
auto name = detail::get_cursor_name(cur);
@ -400,21 +451,21 @@ namespace
if (is_templated_cursor(cur))
return builder.finish(detail::get_entity_id(cur), suffix.body_kind,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
else
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl
|| clang_getTemplateCursorKind(cur) == CXCursor_FunctionDecl,
detail::assert_handler{});
type_safe::optional<cpp_entity_ref> semantic_parent;
return parse_cpp_function_impl(context, cur, false);
return parse_cpp_function_impl(context, cur, false, is_friend);
}
std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
@ -424,7 +475,7 @@ std::unique_ptr<cpp_entity> detail::try_parse_static_cpp_function(
|| clang_getTemplateCursorKind(cur) == CXCursor_CXXMethod,
detail::assert_handler{});
if (clang_CXXMethod_isStatic(cur))
return parse_cpp_function_impl(context, cur, true);
return parse_cpp_function_impl(context, cur, true, false);
return nullptr;
}
@ -516,7 +567,7 @@ namespace
}
std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_CXXMethod
|| clang_getTemplateCursorKind(cur) == CXCursor_CXXMethod,
@ -543,11 +594,11 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_function(const detail::pars
skip_parameters(stream);
return handle_suffix(context, cur, builder, stream, prefix.is_virtual,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
}
std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_ConversionFunction
|| clang_getTemplateCursorKind(cur) == CXCursor_ConversionFunction,
@ -604,11 +655,11 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
builder.is_constexpr();
return handle_suffix(context, cur, builder, stream, prefix.is_virtual,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
}
std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_Constructor
|| clang_getTemplateCursorKind(cur) == CXCursor_Constructor,
@ -643,27 +694,29 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_constructor(const detail::parse_co
if (is_templated_cursor(cur))
return builder.finish(detail::get_entity_id(cur), suffix.body_kind,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
else
return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind,
std::move(prefix.semantic_parent));
parse_scope(cur, is_friend));
}
std::unique_ptr<cpp_entity> detail::parse_cpp_destructor(const detail::parse_context& context,
const CXCursor& cur)
const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_Destructor, detail::assert_handler{});
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
auto is_virtual = detail::skip_if(stream, "virtual");
detail::skip(stream, "~");
auto prefix_info = parse_prefix_info(stream, "~", false);
DEBUG_ASSERT(!prefix_info.is_constexpr && !prefix_info.is_explicit, detail::assert_handler{});
auto name = std::string("~") + stream.get().c_str();
cpp_destructor::builder builder(std::move(name));
context.comments.match(builder.get(), cur);
detail::skip(stream, "(");
detail::skip(stream, ")");
return handle_suffix(context, cur, builder, stream, is_virtual, type_safe::nullopt);
return handle_suffix(context, cur, builder, stream, prefix_info.is_virtual,
parse_scope(cur, is_friend));
}

View file

@ -205,14 +205,16 @@ namespace
auto args = get_arguments(config);
CXTranslationUnit tu;
auto error =
auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing;
if (detail::libclang_compile_config_access::clang_version(config) >= 40000)
flags |= CXTranslationUnit_DetailedPreprocessingRecord;
auto error =
clang_parseTranslationUnit2(idx.get(), path, // index and path
args.data(),
static_cast<int>(args.size()), // arguments (ptr + size)
&file, 1, // unsaved files (ptr + size)
CXTranslationUnit_Incomplete
| CXTranslationUnit_KeepGoing, // flags
&tu);
unsigned(flags), &tu);
if (error != CXError_Success)
{
switch (error)
@ -259,25 +261,42 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
auto file = clang_getFile(tu.get(), path.c_str());
cpp_file::builder builder(path);
auto preprocessed_iter = preprocessed.entities.begin();
auto macro_iter = preprocessed.macros.begin();
auto include_iter = preprocessed.includes.begin();
// convert entity hierachies
detail::parse_context context{tu.get(), file, type_safe::ref(logger()), type_safe::ref(idx),
detail::comment_context(preprocessed.comments)};
detail::visit_tu(tu, path.c_str(), [&](const CXCursor& cur) {
// add macro if needed
for (auto line = get_line_no(cur);
preprocessed_iter != preprocessed.entities.end() && preprocessed_iter->line <= line;
++preprocessed_iter)
builder.add_child(std::move(preprocessed_iter->entity));
if (clang_getCursorKind(cur) == CXCursor_InclusionDirective)
{
DEBUG_ASSERT(include_iter != preprocessed.includes.end()
&& get_line_no(cur) >= include_iter->line,
detail::assert_handler{});
auto entity = detail::parse_entity(context, cur);
if (entity)
builder.add_child(std::move(entity));
auto include =
cpp_include_directive::build(std::move(include_iter->file), include_iter->kind,
detail::get_cursor_name(cur).c_str());
context.comments.match(*include, include_iter->line);
builder.add_child(std::move(include));
++include_iter;
}
else if (clang_getCursorKind(cur) != CXCursor_MacroDefinition)
{
// add macro if needed
for (auto line = get_line_no(cur);
macro_iter != preprocessed.macros.end() && macro_iter->line <= line; ++macro_iter)
builder.add_child(std::move(macro_iter->macro));
auto entity = detail::parse_entity(context, cur);
if (entity)
builder.add_child(std::move(entity));
}
});
for (; preprocessed_iter != preprocessed.entities.end(); ++preprocessed_iter)
builder.add_child(std::move(preprocessed_iter->entity));
for (; macro_iter != preprocessed.macros.end(); ++macro_iter)
builder.add_child(std::move(macro_iter->macro));
for (auto& c : preprocessed.comments)
{

View file

@ -5,6 +5,7 @@
#include "parse_functions.hpp"
#include <cppast/cpp_namespace.hpp>
#include <type_safe/deferred_construction.hpp>
#include "libclang_visitor.hpp"
@ -32,6 +33,7 @@ namespace
return cpp_namespace::builder("", is_inline);
auto& name = stream.get().value();
skip_attribute(stream);
skip(stream, "{");
return cpp_namespace::builder(name.c_str(), is_inline);
}
@ -126,13 +128,14 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
namespace
{
std::vector<cpp_entity_id> parse_entity_target_cursor(const CXCursor& cur)
cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name)
{
std::vector<cpp_entity_id> result;
type_safe::deferred_construction<cpp_entity_ref> result;
detail::visit_children(cur,
[&](const CXCursor& child) {
if (!result.empty())
if (result)
return;
switch (clang_getCursorKind(child))
{
case CXCursor_TypeRef:
@ -142,17 +145,21 @@ namespace
case CXCursor_DeclRefExpr:
{
auto referenced = clang_getCursorReferenced(child);
result.push_back(detail::get_entity_id(referenced));
result = cpp_entity_ref(detail::get_entity_id(referenced),
std::move(name));
break;
}
case CXCursor_OverloadedDeclRef:
{
auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::assert_handler{});
DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur,
"no target for using declaration");
std::vector<cpp_entity_id> ids;
for (auto i = 0u; i != size; ++i)
result.push_back(detail::get_entity_id(
ids.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, i)));
result = cpp_entity_ref(std::move(ids), std::move(name));
break;
}
@ -166,7 +173,7 @@ namespace
},
true);
return result;
return result.value();
}
}
@ -186,7 +193,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_declaration(
while (!stream.done() && !detail::skip_if(stream, ";"))
target_name += stream.get().c_str();
auto target = cpp_entity_ref(parse_entity_target_cursor(cur), std::move(target_name));
auto target = parse_entity_target_cursor(cur, std::move(target_name));
auto result = cpp_using_declaration::build(std::move(target));
context.comments.match(*result, cur);
return std::move(result);

View file

@ -23,6 +23,15 @@ cpp_entity_id detail::get_entity_id(const CXCursor& cur)
cxstring type_spelling(clang_getTypeSpelling(clang_getCursorResultType(cur)));
return cpp_entity_id(std::string(usr.c_str()) + type_spelling.c_str());
}
else if (clang_getCursorKind(cur) == CXCursor_ClassTemplatePartialSpecialization)
{
// libclang issue: templ<T()> vs templ<T() &>
// but identical USR
// same workaround: combine display name with usr
// (and hope this prevents all collisions...)
cxstring display_name(clang_getCursorDisplayName(cur));
return cpp_entity_id(std::string(usr.c_str()) + display_name.c_str());
}
else
return cpp_entity_id(usr.c_str());
}
@ -82,6 +91,19 @@ void detail::comment_context::match(cpp_entity& e, unsigned line) const
e.set_comment(std::move(cur_++->comment));
}
namespace
{
bool is_friend(const CXCursor& parent_cur)
{
#if CPPAST_CINDEX_HAS_FRIEND
return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl;
#else
(void)parent_cur;
return false;
#endif
}
}
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
const CXCursor& cur,
const CXCursor& parent_cur) try
@ -104,6 +126,11 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
return entity;
break;
case CXCursor_MacroDefinition:
case CXCursor_InclusionDirective:
DEBUG_UNREACHABLE(detail::assert_handler{}, "handle preprocessor in parser callback");
break;
case CXCursor_Namespace:
return parse_cpp_namespace(context, cur);
case CXCursor_NamespaceAlias:
@ -131,25 +158,29 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
return parse_cpp_member_variable(context, cur);
case CXCursor_FunctionDecl:
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_function(context, cur);
return parse_cpp_function(context, cur, is_friend(parent_cur));
case CXCursor_CXXMethod:
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
else if (auto func = try_parse_static_cpp_function(context, cur))
return func;
return parse_cpp_member_function(context, cur);
return parse_cpp_member_function(context, cur, is_friend(parent_cur));
case CXCursor_ConversionFunction:
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_conversion_op(context, cur);
return parse_cpp_conversion_op(context, cur, is_friend(parent_cur));
case CXCursor_Constructor:
if (auto tfunc = try_parse_cpp_function_template_specialization(context, cur))
if (auto tfunc =
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc;
return parse_cpp_constructor(context, cur);
return parse_cpp_constructor(context, cur, is_friend(parent_cur));
case CXCursor_Destructor:
return parse_cpp_destructor(context, cur);
return parse_cpp_destructor(context, cur, is_friend(parent_cur));
#if CPPAST_CINDEX_HAS_FRIEND
case CXCursor_FriendDecl:
@ -159,7 +190,7 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_TypeAliasTemplateDecl:
return parse_cpp_alias_template(context, cur);
case CXCursor_FunctionTemplate:
return parse_cpp_function_template(context, cur);
return parse_cpp_function_template(context, cur, is_friend(parent_cur));
case CXCursor_ClassTemplate:
return parse_cpp_class_template(context, cur);
case CXCursor_ClassTemplatePartialSpecialization:
@ -172,21 +203,27 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
break;
}
auto msg = detail::format("unhandled cursor of kind '",
detail::get_cursor_kind_spelling(cur).c_str(), "'");
context.logger->log("libclang parser",
diagnostic{std::move(msg), detail::make_location(cur), severity::warning});
if (!clang_isAttribute(clang_getCursorKind(cur)))
{
auto msg = detail::format("unhandled cursor of kind '",
detail::get_cursor_kind_spelling(cur).c_str(), "'");
context.logger->log("libclang parser",
diagnostic{std::move(msg), detail::make_location(cur),
severity::warning});
// build unexposed entity
auto name = detail::get_cursor_name(cur);
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
auto spelling = detail::to_string(stream, stream.end());
if (name.empty())
return cpp_unexposed_entity::build(std::move(spelling));
// build unexposed entity
auto name = detail::get_cursor_name(cur);
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
auto spelling = detail::to_string(stream, stream.end());
if (name.empty())
return cpp_unexposed_entity::build(std::move(spelling));
else
return cpp_unexposed_entity::build(*context.idx, detail::get_entity_id(cur),
name.c_str(), std::move(spelling));
}
else
return cpp_unexposed_entity::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(spelling));
return nullptr;
}
catch (parse_error& ex)
{

View file

@ -103,7 +103,7 @@ namespace cppast
// on all function cursors except on destructor
std::unique_ptr<cpp_entity> try_parse_cpp_function_template_specialization(
const parse_context& context, const CXCursor& cur);
const parse_context& context, const CXCursor& cur, bool is_friend);
// on class cursors
std::unique_ptr<cpp_entity> try_parse_full_cpp_class_template_specialization(
@ -134,15 +134,15 @@ namespace cppast
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_conversion_op(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_constructor(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_friend(const parse_context& context,
const CXCursor& cur);
@ -150,7 +150,8 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function_template(const parse_context& context,
const CXCursor& cur);
const CXCursor& cur,
bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_class_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class_template_specialization(

View file

@ -25,6 +25,11 @@ namespace ts = type_safe;
namespace
{
std::string quote(std::string str)
{
return '"' + std::move(str) + '"';
}
// build the command that runs the preprocessor
std::string get_command(const libclang_compile_config& c, const char* full_path)
{
@ -41,8 +46,9 @@ namespace
// -fno-show-column: don't show the column number
// -fdiagnostics-format msvc: use easier to parse MSVC format
flags += " -fno-caret-diagnostics -fno-show-column -fdiagnostics-format=msvc";
// -Wno-pragma-once-outside-header: hide wrong warning
flags += " -Wno-pragma-once-outside-header";
// -Wno-*: hide wrong warnings if header file is directly parsed
flags += " -Wno-pragma-once-outside-header -Wno-pragma-system-header-outside-header "
"-Wno-include-next-outside-header";
std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " "
+ std::move(flags) + " ");
@ -55,7 +61,7 @@ namespace
}
// add path to file being processed
cmd += full_path;
cmd += quote(full_path);
return cmd;
}
@ -210,8 +216,13 @@ namespace
void bump(std::size_t offset) noexcept
{
for (std::size_t i = 0u; i != offset; ++i)
bump();
if (write_ == true)
{
for (std::size_t i = 0u; i != offset; ++i)
bump();
}
else
skip(offset);
}
// no write, no newline detection
@ -257,17 +268,106 @@ namespace
ts::flag write_;
};
bool starts_with(const position& p, const char* str)
bool starts_with(const position& p, const char* str, std::size_t len)
{
return std::strncmp(p.ptr(), str, std::strlen(str)) == 0;
return std::strncmp(p.ptr(), str, len) == 0;
}
template <std::size_t N>
bool starts_with(const position& p, const char (&str)[N])
{
return std::strncmp(p.ptr(), str, N - 1) == 0;
}
void skip(position& p, const char* str)
{
DEBUG_ASSERT(starts_with(p, str), detail::assert_handler{});
DEBUG_ASSERT(starts_with(p, str, std::strlen(str)), detail::assert_handler{});
p.skip(std::strlen(str));
}
void skip_spaces(position& p, bool bump = false)
{
while (starts_with(p, " "))
if (bump)
p.bump();
else
p.skip();
}
struct linemarker
{
std::string file;
unsigned line;
enum
{
line_directive, // no change in file
enter_new, // open a new file
enter_old, // return to an old file
} flag = line_directive;
bool is_system = false;
};
ts::optional<linemarker> parse_linemarker(position& p)
{
// format (at new line): # <line> "<filename>" <flags>
// flag 1: enter_new
// flag 2: enter_old
// flag 3: system file
// flag 4: ignored
if (!p.was_newl() || !starts_with(p, "#"))
return ts::nullopt;
p.skip();
DEBUG_ASSERT(!starts_with(p, "define") && !starts_with(p, "undef")
&& !starts_with(p, "pragma"),
detail::assert_handler{}, "handle macros first");
linemarker result;
std::string line;
for (skip_spaces(p); std::isdigit(*p.ptr()); p.skip())
line += *p.ptr();
result.line = unsigned(std::stoi(line));
skip_spaces(p);
DEBUG_ASSERT(*p.ptr() == '"', detail::assert_handler{});
p.skip();
std::string file_name;
for (; !starts_with(p, "\""); p.skip())
file_name += *p.ptr();
p.skip();
result.file = std::move(file_name);
for (; !starts_with(p, "\n"); p.skip())
{
skip_spaces(p);
switch (*p.ptr())
{
case '1':
DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{});
result.flag = linemarker::enter_new;
break;
case '2':
DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{});
result.flag = linemarker::enter_old;
break;
case '3':
result.is_system = true;
break;
case '4':
break; // ignored
default:
DEBUG_UNREACHABLE(detail::assert_handler{}, "invalid line marker");
break;
}
}
p.skip();
return result;
}
detail::pp_doc_comment parse_c_doc_comment(position& p)
{
detail::pp_doc_comment result;
@ -415,23 +515,13 @@ namespace
}
else
{
while (!starts_with(p, "\n"))
p.skip();
// don't skip newline
auto newline = std::strchr(p.ptr(), '\n');
p.skip(std::size_t(newline - p.ptr())); // don't skip newline
}
return true;
}
void skip_spaces(position& p, bool bump = false)
{
while (starts_with(p, " "))
if (bump)
p.bump();
else
p.skip();
}
std::unique_ptr<cpp_macro_definition> parse_macro(position& p,
detail::preprocessor_output& output,
bool in_main_file)
@ -504,15 +594,13 @@ namespace
return result;
}
std::unique_ptr<cpp_include_directive> parse_include(position& p,
detail::preprocessor_output& output,
bool in_main_file)
type_safe::optional<detail::pp_include> parse_include(position& p, bool in_main_file)
{
// format (at new line, literal <>): #include <filename>
// or: #include "filename"
// note: don't write include back
if (!p.was_newl() || !starts_with(p, "#include"))
return nullptr;
return type_safe::nullopt;
p.skip(std::strlen("#include"));
if (starts_with(p, "_next"))
p.skip(std::strlen("_next"));
@ -537,23 +625,18 @@ namespace
std::string filename;
for (; !starts_with(p, "\"") && !starts_with(p, ">"); p.skip())
filename += *p.ptr();
DEBUG_ASSERT(starts_with(p, end_str), detail::assert_handler{}, "bad termination");
DEBUG_ASSERT(starts_with(p, end_str, std::strlen(end_str)), detail::assert_handler{},
"bad termination");
p.skip();
skip(p, " /* clang -E -dI */");
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
// don't skip newline
if (!in_main_file)
return nullptr;
return type_safe::nullopt;
auto result = cpp_include_directive::build(cpp_file_ref(cpp_entity_id(filename), filename),
include_kind);
if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line()))
{
result->set_comment(std::move(output.comments.back().comment));
output.comments.pop_back();
}
return result;
return detail::pp_include{cpp_file_ref(cpp_entity_id(filename), filename), include_kind,
p.cur_line()};
}
bool bump_pragma(position& p)
@ -568,80 +651,6 @@ namespace
return true;
}
struct linemarker
{
std::string file;
unsigned line;
enum
{
line_directive, // no change in file
enter_new, // open a new file
enter_old, // return to an old file
} flag = line_directive;
bool is_system = false;
};
ts::optional<linemarker> parse_linemarker(position& p)
{
// format (at new line): # <line> "<filename>" <flags>
// flag 1: enter_new
// flag 2: enter_old
// flag 3: system file
// flag 4: ignored
if (!p.was_newl() || !starts_with(p, "#"))
return ts::nullopt;
p.skip();
DEBUG_ASSERT(!starts_with(p, "define") && !starts_with(p, "undef")
&& !starts_with(p, "pragma"),
detail::assert_handler{}, "handle macros first");
linemarker result;
std::string line;
for (skip_spaces(p); std::isdigit(*p.ptr()); p.skip())
line += *p.ptr();
result.line = unsigned(std::stoi(line));
skip_spaces(p);
DEBUG_ASSERT(*p.ptr() == '"', detail::assert_handler{});
p.skip();
std::string file_name;
for (; !starts_with(p, "\""); p.skip())
file_name += *p.ptr();
p.skip();
result.file = std::move(file_name);
for (; !starts_with(p, "\n"); p.skip())
{
skip_spaces(p);
switch (*p.ptr())
{
case '1':
DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{});
result.flag = linemarker::enter_new;
break;
case '2':
DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{});
result.flag = linemarker::enter_old;
break;
case '3':
result.is_system = true;
break;
case '4':
break; // ignored
default:
DEBUG_UNREACHABLE(detail::assert_handler{}, "invalid line marker");
break;
}
}
p.skip();
return result;
}
}
detail::preprocessor_output detail::preprocess(const libclang_compile_config& config,
@ -656,6 +665,10 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
ts::flag in_string(false), in_char(false);
while (p)
{
auto next = std::strpbrk(p.ptr(), "\"'#/"); // look for ", ', # or /
if (next && next > p.ptr())
p.bump(std::size_t(next - p.ptr() - 1)); // subtract one to get before that character
if (starts_with(p, "\\\"")) // starts with \"
p.bump(2u);
else if (starts_with(p, "\\'")) // starts with \'
@ -682,7 +695,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
severity::debug});
}
result.entities.push_back({std::move(macro), p.cur_line()});
result.macros.push_back({std::move(macro), p.cur_line()});
}
else if (auto undef = parse_undef(p))
{
@ -695,26 +708,24 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
diagnostic{std::move(message), source_location::make_file(path),
severity::debug});
}
result.entities
.erase(std::remove_if(result.entities.begin(), result.entities.end(),
[&](const pp_entity& e) {
return e.entity->kind()
== cpp_entity_kind::macro_definition_t
&& e.entity->name() == undef.value();
}),
result.entities.end());
result.macros.erase(std::remove_if(result.macros.begin(), result.macros.end(),
[&](const pp_macro& e) {
return e.macro->name() == undef.value();
}),
result.macros.end());
}
}
else if (auto include = parse_include(p, result, file_depth == 0u))
else if (auto include = parse_include(p, file_depth == 0u))
{
if (logger.is_verbose())
{
auto message = detail::format("parsing include '", include->name(), "'");
auto message =
detail::format("parsing include '", include.value().file.name(), "'");
logger.log("preprocessor",
diagnostic{std::move(message), source_location::make_file(path),
severity::debug});
}
result.entities.push_back({std::move(include), p.cur_line()});
result.includes.push_back(std::move(include.value()));
}
else if (bump_pragma(p))
continue;

View file

@ -12,10 +12,17 @@ namespace cppast
{
namespace detail
{
struct pp_entity
struct pp_macro
{
std::unique_ptr<cpp_entity> entity;
unsigned line;
std::unique_ptr<cpp_macro_definition> macro;
unsigned line;
};
struct pp_include
{
cpp_file_ref file;
cpp_include_kind kind;
unsigned line;
};
struct pp_doc_comment
@ -35,7 +42,8 @@ namespace cppast
struct preprocessor_output
{
std::string source;
std::vector<pp_entity> entities;
std::vector<pp_include> includes;
std::vector<pp_macro> macros;
std::vector<pp_doc_comment> comments;
};

View file

@ -216,7 +216,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse
}
std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
const detail::parse_context& context, const CXCursor& cur)
const detail::parse_context& context, const CXCursor& cur, bool is_friend)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionTemplate, detail::assert_handler{});
@ -224,19 +224,19 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
switch (clang_getTemplateCursorKind(cur))
{
case CXCursor_FunctionDecl:
func = detail::parse_cpp_function(context, cur);
func = detail::parse_cpp_function(context, cur, is_friend);
break;
case CXCursor_CXXMethod:
if (auto sfunc = detail::try_parse_static_cpp_function(context, cur))
func = std::move(sfunc);
else
func = detail::parse_cpp_member_function(context, cur);
func = detail::parse_cpp_member_function(context, cur, is_friend);
break;
case CXCursor_ConversionFunction:
func = detail::parse_cpp_conversion_op(context, cur);
func = detail::parse_cpp_conversion_op(context, cur, is_friend);
break;
case CXCursor_Constructor:
func = detail::parse_cpp_constructor(context, cur);
func = detail::parse_cpp_constructor(context, cur, is_friend);
break;
default:
@ -284,7 +284,7 @@ namespace
}
std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specialization(
const detail::parse_context& context, const CXCursor& cur)
const detail::parse_context& context, const CXCursor& cur, bool is_friend)
{
auto templ = clang_getSpecializedCursorTemplate(cur);
if (clang_Cursor_isNull(templ))
@ -294,19 +294,19 @@ std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specializati
switch (clang_getCursorKind(cur))
{
case CXCursor_FunctionDecl:
func = detail::parse_cpp_function(context, cur);
func = detail::parse_cpp_function(context, cur, is_friend);
break;
case CXCursor_CXXMethod:
if (auto sfunc = detail::try_parse_static_cpp_function(context, cur))
func = std::move(sfunc);
else
func = detail::parse_cpp_member_function(context, cur);
func = detail::parse_cpp_member_function(context, cur, is_friend);
break;
case CXCursor_ConversionFunction:
func = detail::parse_cpp_conversion_op(context, cur);
func = detail::parse_cpp_conversion_op(context, cur, is_friend);
break;
case CXCursor_Constructor:
func = detail::parse_cpp_constructor(context, cur);
func = detail::parse_cpp_constructor(context, cur, is_friend);
break;
default:

View file

@ -135,6 +135,9 @@ namespace
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
begin = get_next_location(tu, file, begin, -1);
else if (kind == CXCursor_Destructor && token_after_is(tu, file, cur, end, ")"))
// necessary for some other reason
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, cur, end, "("))
{
@ -285,6 +288,20 @@ bool detail::skip_if(detail::token_stream& stream, const char* str, bool multi_t
return true;
}
namespace
{
// whether or not the current angle bracket can be a comparison
// note: this is a heuristic I hope works often enough
bool is_comparison(CXTokenKind last_kind, const detail::token& cur, CXTokenKind next_kind)
{
if (cur == "<")
return last_kind == CXToken_Literal;
else if (cur == ">")
return next_kind == CXToken_Literal;
return false;
}
}
detail::token_iterator detail::find_closing_bracket(detail::token_stream stream)
{
auto template_bracket = false;
@ -307,12 +324,15 @@ detail::token_iterator detail::find_closing_bracket(detail::token_stream stream)
auto bracket_count = 1;
auto paren_count = 0; // internal nested parenthesis
auto last_token = CXToken_Comment;
while (!stream.done() && bracket_count != 0)
{
auto& cur = stream.get().value();
if (paren_count == 0 && cur == open_bracket)
auto& cur = stream.get();
if (paren_count == 0 && cur == open_bracket
&& !is_comparison(last_token, cur, stream.peek().kind()))
++bracket_count;
else if (paren_count == 0 && cur == close_bracket)
else if (paren_count == 0 && cur == close_bracket
&& !is_comparison(last_token, cur, stream.peek().kind()))
--bracket_count;
else if (paren_count == 0 && template_bracket && cur == ">>")
// maximal munch
@ -321,6 +341,8 @@ detail::token_iterator detail::find_closing_bracket(detail::token_stream stream)
++paren_count;
else if (cur == ")" || cur == "}" || cur == "]")
--paren_count;
last_token = cur.kind();
}
stream.bump_back();
// only check first parameter, token might be ">>"

View file

@ -468,7 +468,6 @@ namespace
case CXType_ObjCId:
case CXType_ObjCClass:
case CXType_ObjCSel:
case CXType_Complex:
case CXType_BlockPointer:
case CXType_Vector:
case CXType_ObjCInterface:
@ -498,6 +497,8 @@ namespace
else if (auto ptype = try_parse_template_parameter_type(context, cur, type))
// template parameter type is unexposed
return ptype;
// fallthrough
case CXType_Complex:
return cpp_unexposed_type::build(get_type_spelling(type).c_str());
case CXType_Void:

View file

@ -9,24 +9,25 @@ if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/catch.hpp)
endif()
set(tests
code_generator.cpp
cpp_alias_template.cpp
cpp_class.cpp
cpp_class_template.cpp
cpp_enum.cpp
cpp_friend.cpp
cpp_function.cpp
cpp_function_template.cpp
cpp_language_linkage.cpp
cpp_member_function.cpp
cpp_member_variable.cpp
cpp_namespace.cpp
cpp_preprocessor.cpp
cpp_static_assert.cpp
cpp_template_parameter.cpp
cpp_type_alias.cpp
cpp_variable.cpp
visitor.cpp)
code_generator.cpp
cpp_alias_template.cpp
cpp_class.cpp
cpp_class_template.cpp
cpp_enum.cpp
cpp_friend.cpp
cpp_function.cpp
cpp_function_template.cpp
cpp_language_linkage.cpp
cpp_member_function.cpp
cpp_member_variable.cpp
cpp_namespace.cpp
cpp_preprocessor.cpp
cpp_static_assert.cpp
cpp_template_parameter.cpp
cpp_type_alias.cpp
cpp_variable.cpp
visitor.cpp
integration.cpp)
add_executable(cppast_test test.cpp test_parser.hpp ${tests})
target_include_directories(cppast_test PUBLIC ${CMAKE_CURRENT_BINARY_DIR})

View file

@ -331,6 +331,10 @@ struct d : c
/// virtual ~d() override final;
~d() final;
};
/// virtual d::~d();
d::~d() {}
)";
auto file = parse({}, "cpp_destructor.cpp", code);
@ -364,12 +368,15 @@ struct d : c
else if (dtor.name() == "~d")
{
REQUIRE(dtor.virtual_info());
REQUIRE(dtor.virtual_info().value()
== (cpp_virtual_flags::override | cpp_virtual_flags::final));
if (dtor.is_declaration())
REQUIRE(dtor.virtual_info().value()
== (cpp_virtual_flags::override | cpp_virtual_flags::final));
else
REQUIRE(dtor.virtual_info().value() == cpp_virtual_flags::override);
REQUIRE(!dtor.noexcept_condition());
}
else
REQUIRE(false);
});
REQUIRE(count == 4u);
REQUIRE(count == 5u);
}

View file

@ -107,12 +107,14 @@ TEST_CASE("cpp_include_directive", "[!hide][clang4]")
REQUIRE(include.target().name() == include.name());
REQUIRE(include.include_kind() == cppast::cpp_include_kind::system);
REQUIRE(include.target().get(idx).empty());
REQUIRE_THAT(include.full_path(), Catch::EndsWith("iostream"));
}
else if (include.name() == "cpp_include_directive-header.hpp")
{
REQUIRE(include.target().name() == include.name());
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
REQUIRE(include.target().get(idx).empty());
REQUIRE(include.full_path() == "./cpp_include_directive-header.hpp");
}
else
REQUIRE(false);
@ -126,6 +128,7 @@ TEST_CASE("cpp_include_directive", "[!hide][clang4]")
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
REQUIRE(
equal_ref(idx, include.target(), cpp_file_ref(cpp_entity_id(""), "header_a.hpp")));
REQUIRE(include.full_path() == "./header_a.hpp");
}
else
REQUIRE(false);

View file

@ -149,11 +149,15 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
return false;
else if (!inst_parsed.arguments_exposed())
return inst_parsed.unexposed_arguments() == inst_synthesized.unexposed_arguments();
else if (inst_parsed.arguments().has_value() != inst_synthesized.arguments().has_value())
return false;
else if (!inst_parsed.arguments().has_value())
return true;
auto iter_a = inst_parsed.arguments().begin();
auto iter_b = inst_synthesized.arguments().begin();
while (iter_a != inst_parsed.arguments().end()
&& iter_b != inst_synthesized.arguments().end())
auto iter_a = inst_parsed.arguments().value().begin();
auto iter_b = inst_synthesized.arguments().value().begin();
while (iter_a != inst_parsed.arguments().value().end()
&& iter_b != inst_synthesized.arguments().value().end())
{
if (iter_a->type().has_value() && iter_b->type().has_value())
{
@ -175,8 +179,8 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
++iter_a;
++iter_b;
}
return iter_a == inst_parsed.arguments().end()
&& iter_b == inst_synthesized.arguments().end();
return iter_a == inst_parsed.arguments().value().end()
&& iter_b == inst_synthesized.arguments().value().end();
}
case cpp_type_kind::dependent_t:
{

118
test/integration.cpp Normal file
View file

@ -0,0 +1,118 @@
// 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 "test_parser.hpp"
#include <cppast/cpp_preprocessor.hpp>
using namespace cppast;
void parse_included_files(const cpp_entity_index& idx, const cpp_file& file)
{
for (auto& e : file)
{
if (e.kind() == cpp_entity_kind::include_directive_t)
{
auto path = static_cast<const cpp_include_directive&>(e).full_path();
parse_file(idx, path.c_str());
}
}
}
TEST_CASE("stdlib", "[!hide][integration]")
{
auto code = R"(
// list of headers from: http://en.cppreference.com/w/cpp/header
//#include <cstdlib> -- problem with compiler built-in stuff on OSX
#include <csignal>
//#include <csetjmp> -- same as above
#include <cstdarg>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include <bitset>
#include <functional>
#include <utility>
#include <ctime>
#include <chrono>
#include <cstddef>
#include <initializer_list>
#include <tuple>
#include <new>
#include <memory>
#include <scoped_allocator>
#include <climits>
#include <cfloat>
#include <cstdint>
//#include <cinttypes> -- missing types from C header (for some reason)
#include <limits>
//#include <exception> -- weird issue with compiler built-in stuff
#include <stdexcept>
#include <cassert>
#include <system_error>
#include <cerrno>
#include <cctype>
#include <cwctype>
#include <cstring>
#include <cwchar>
//#include <cuchar> -- not supported on CI
#include <string>
#include <array>
#include <vector>
#include <deque>
#include <list>
#include <forward_list>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <algorithm>
#include <iterator>
//#include <cmath> -- non-conforming GCC extension with regards to constexpr
//#include <complex> -- weird double include issue under MSVC
#include <valarray>
#include <random>
#include <numeric>
#include <ratio>
//#include <cfenv> -- same issue with cinttypes
#include <iosfwd>
#include <ios>
#include <istream>
#include <ostream>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <streambuf>
#include <cstdio>
#include <locale>
//#include <clocale> -- issue on OSX
#include <regex>
//#include <atomic> -- issue on MSVC
#include <thread>
#include <mutex>
#include <future>
#include <condition_variable>
)";
cpp_entity_index idx;
auto file = parse(idx, "stdlib.cpp", code);
parse_included_files(idx, *file);
}

View file

@ -23,13 +23,11 @@ inline void write_file(const char* name, const char* code)
file << code;
}
inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& idx,
const char* name, const char* code)
inline std::unique_ptr<cppast::cpp_file> parse_file(const cppast::cpp_entity_index& idx,
const char* name)
{
using namespace cppast;
write_file(name, code);
libclang_compile_config config;
config.set_flags(cpp_standard::cpp_latest);
@ -42,6 +40,13 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
return result;
}
inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& idx,
const char* name, const char* code)
{
write_file(name, code);
return parse_file(idx, name);
}
class test_generator : public cppast::code_generator
{
public: