Add integration test that parses the standard library
This commit is contained in:
commit
70ac5cf111
24 changed files with 650 additions and 321 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(">");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 ">>"
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
118
test/integration.cpp
Normal 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);
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue