Fix and test include directive parsing
This commit is contained in:
parent
dc5abeb785
commit
9c46c96820
7 changed files with 90 additions and 21 deletions
3
external/external.cmake
vendored
3
external/external.cmake
vendored
|
|
@ -84,7 +84,7 @@ function(_cppast_find_llvm_config)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# find libclang using the config tool
|
# find libclang using the config tool
|
||||||
# sets: LIBCLANG_INCLUDE_DIR, LIBCLANG_SYSTEM_INCLUDE_DIR, LIBCLANG_LIBRARY and CLANG_BINARY
|
# sets: LLVM_VERSION, LIBCLANG_INCLUDE_DIR, LIBCLANG_SYSTEM_INCLUDE_DIR, LIBCLANG_LIBRARY and CLANG_BINARY
|
||||||
function(_cppast_find_libclang config_tool min_version force)
|
function(_cppast_find_libclang config_tool min_version force)
|
||||||
# check version
|
# check version
|
||||||
execute_process(COMMAND ${LLVM_CONFIG_BINARY} --version
|
execute_process(COMMAND ${LLVM_CONFIG_BINARY} --version
|
||||||
|
|
@ -93,6 +93,7 @@ function(_cppast_find_libclang config_tool min_version force)
|
||||||
message(FATAL_ERROR "Outdated LLVM version ${llvm_version}, minimal supported is ${min_version}")
|
message(FATAL_ERROR "Outdated LLVM version ${llvm_version}, minimal supported is ${min_version}")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using LLVM version ${llvm_version}")
|
message(STATUS "Using LLVM version ${llvm_version}")
|
||||||
|
set(LLVM_VERSION ${llvm_version} CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# get include directory
|
# get include directory
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ namespace cppast
|
||||||
{
|
{
|
||||||
static const std::string& clang_binary(const libclang_compile_config& config);
|
static const std::string& clang_binary(const libclang_compile_config& config);
|
||||||
|
|
||||||
|
static int clang_version(const libclang_compile_config& config);
|
||||||
|
|
||||||
static const std::vector<std::string>& flags(const libclang_compile_config& config);
|
static const std::vector<std::string>& flags(const libclang_compile_config& config);
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
@ -29,11 +31,12 @@ namespace cppast
|
||||||
public:
|
public:
|
||||||
libclang_compile_config();
|
libclang_compile_config();
|
||||||
|
|
||||||
/// \effects Sets the path to the location of the `clang++` binary.
|
/// \effects Sets the path to the location of the `clang++` binary and the version of that binary.
|
||||||
/// \notes It will be used for preprocessing.
|
/// \notes It will be used for preprocessing.
|
||||||
void set_clang_binary(std::string binary)
|
void set_clang_binary(std::string binary, int major, int minor, int patch)
|
||||||
{
|
{
|
||||||
clang_binary_ = std::move(binary);
|
clang_binary_ = std::move(binary);
|
||||||
|
clang_version_ = major * 10000 + minor * 100 + patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -51,6 +54,7 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string clang_binary_;
|
std::string clang_binary_;
|
||||||
|
int clang_version_;
|
||||||
|
|
||||||
friend detail::libclang_compile_config_access;
|
friend detail::libclang_compile_config_access;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -96,5 +96,6 @@ target_include_directories(cppast PUBLIC ../include)
|
||||||
target_link_libraries(cppast PUBLIC type_safe _cppast_tiny_process _cppast_libclang)
|
target_link_libraries(cppast PUBLIC type_safe _cppast_tiny_process _cppast_libclang)
|
||||||
target_compile_definitions(cppast PUBLIC
|
target_compile_definitions(cppast PUBLIC
|
||||||
CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR="${LIBCLANG_SYSTEM_INCLUDE_DIR}"
|
CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR="${LIBCLANG_SYSTEM_INCLUDE_DIR}"
|
||||||
CPPAST_CLANG_BINARY="${CLANG_BINARY}")
|
CPPAST_CLANG_BINARY="${CLANG_BINARY}"
|
||||||
|
CPPAST_CLANG_VERSION_STRING="${LLVM_VERSION}")
|
||||||
set_target_properties(cppast PROPERTIES CXX_STANDARD 11)
|
set_target_properties(cppast PROPERTIES CXX_STANDARD 11)
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,39 @@ const std::string& detail::libclang_compile_config_access::clang_binary(
|
||||||
return config.clang_binary_;
|
return config.clang_binary_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int detail::libclang_compile_config_access::clang_version(const libclang_compile_config& config)
|
||||||
|
{
|
||||||
|
return config.clang_version_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<std::string>& detail::libclang_compile_config_access::flags(
|
const std::vector<std::string>& detail::libclang_compile_config_access::flags(
|
||||||
const libclang_compile_config& config)
|
const libclang_compile_config& config)
|
||||||
{
|
{
|
||||||
return config.get_flags();
|
return config.get_flags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int parse_number(const char*& str)
|
||||||
|
{
|
||||||
|
auto result = 0;
|
||||||
|
for (; *str && *str != '.'; ++str)
|
||||||
|
{
|
||||||
|
result *= 10;
|
||||||
|
result += int(*str - '0');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
libclang_compile_config::libclang_compile_config() : compile_config({})
|
libclang_compile_config::libclang_compile_config() : compile_config({})
|
||||||
{
|
{
|
||||||
set_clang_binary(CPPAST_CLANG_BINARY);
|
auto ptr = CPPAST_CLANG_VERSION_STRING;
|
||||||
|
auto major = parse_number(ptr);
|
||||||
|
auto minor = parse_number(ptr);
|
||||||
|
auto patch = parse_number(ptr);
|
||||||
|
set_clang_binary(CPPAST_CLANG_BINARY, major, minor, patch);
|
||||||
|
|
||||||
add_include_dir(CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR);
|
add_include_dir(CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,14 +35,19 @@ namespace
|
||||||
// -E: print preprocessor output
|
// -E: print preprocessor output
|
||||||
// -CC: keep comments, even in macro
|
// -CC: keep comments, even in macro
|
||||||
// -dD: print macro definitions as well
|
// -dD: print macro definitions as well
|
||||||
// -dI: print include directives as well
|
auto flags = std::string("-E -CC -dD");
|
||||||
|
if (detail::libclang_compile_config_access::clang_version(c) >= 40000)
|
||||||
|
// -Xclang -dI: print include directives as well (clang >= 4.0.0)
|
||||||
|
flags += " -Xclang -dI";
|
||||||
// -fno-caret-diagnostics: don't show the source extract in diagnostics
|
// -fno-caret-diagnostics: don't show the source extract in diagnostics
|
||||||
// -fno-show-column: don't show the column number
|
// -fno-show-column: don't show the column number
|
||||||
// -fdiagnostics-format=msvc: use easier to parse MSVC format
|
// -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
|
// -Wno-pragma-once-outside-header: hide wrong warning
|
||||||
std::string cmd(detail::libclang_compile_config_access::clang_binary(c)
|
flags += " -Wno-pragma-once-outside-header";
|
||||||
+ " -E -CC -dD -dI -fno-caret-diagnostics -fno-show-column "
|
|
||||||
"-fdiagnostics-format=msvc -Wno-pragma-once-outside-header ");
|
std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " "
|
||||||
|
+ std::move(flags) + " ");
|
||||||
|
|
||||||
// add other flags
|
// add other flags
|
||||||
for (auto& flag : detail::libclang_compile_config_access::flags(c))
|
for (auto& flag : detail::libclang_compile_config_access::flags(c))
|
||||||
|
|
@ -61,10 +66,17 @@ namespace
|
||||||
{
|
{
|
||||||
// format: <filename>(<line>):
|
// format: <filename>(<line>):
|
||||||
// or: <filename>:
|
// or: <filename>:
|
||||||
|
auto fallback = ptr;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
while (*ptr && *ptr != ':' && *ptr != '(')
|
while (*ptr && *ptr != ':' && *ptr != '(')
|
||||||
filename.push_back(*ptr++);
|
filename.push_back(*ptr++);
|
||||||
|
|
||||||
|
if (filename == "error" || filename == "warning" || filename == "fatal error")
|
||||||
|
{
|
||||||
|
ptr = fallback;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
type_safe::optional<unsigned> line;
|
type_safe::optional<unsigned> line;
|
||||||
if (*ptr == '(')
|
if (*ptr == '(')
|
||||||
{
|
{
|
||||||
|
|
@ -87,6 +99,7 @@ namespace
|
||||||
severity parse_severity(const char*& ptr)
|
severity parse_severity(const char*& ptr)
|
||||||
{
|
{
|
||||||
// format: <severity>:
|
// format: <severity>:
|
||||||
|
auto fallback = ptr;
|
||||||
std::string sev;
|
std::string sev;
|
||||||
while (*ptr && *ptr != ':')
|
while (*ptr && *ptr != ':')
|
||||||
sev.push_back(*ptr++);
|
sev.push_back(*ptr++);
|
||||||
|
|
@ -99,7 +112,7 @@ namespace
|
||||||
else if (sev == "fatal error")
|
else if (sev == "fatal error")
|
||||||
return severity::critical;
|
return severity::critical;
|
||||||
else
|
else
|
||||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
ptr = fallback;
|
||||||
return severity::error;
|
return severity::error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,6 +262,12 @@ namespace
|
||||||
return std::strncmp(p.ptr(), str, std::strlen(str)) == 0;
|
return std::strncmp(p.ptr(), str, std::strlen(str)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void skip(position& p, const char* str)
|
||||||
|
{
|
||||||
|
DEBUG_ASSERT(starts_with(p, str), detail::assert_handler{});
|
||||||
|
p.skip(std::strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
detail::pp_doc_comment parse_c_doc_comment(position& p)
|
detail::pp_doc_comment parse_c_doc_comment(position& p)
|
||||||
{
|
{
|
||||||
detail::pp_doc_comment result;
|
detail::pp_doc_comment result;
|
||||||
|
|
@ -484,6 +503,8 @@ namespace
|
||||||
if (!p.was_newl() || !starts_with(p, "#include"))
|
if (!p.was_newl() || !starts_with(p, "#include"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
p.skip(std::strlen("#include"));
|
p.skip(std::strlen("#include"));
|
||||||
|
if (starts_with(p, "_next"))
|
||||||
|
p.skip(std::strlen("_next"));
|
||||||
skip_spaces(p);
|
skip_spaces(p);
|
||||||
|
|
||||||
auto include_kind = cpp_include_kind::system;
|
auto include_kind = cpp_include_kind::system;
|
||||||
|
|
@ -507,6 +528,7 @@ namespace
|
||||||
filename += *p.ptr();
|
filename += *p.ptr();
|
||||||
DEBUG_ASSERT(starts_with(p, end_str), detail::assert_handler{}, "bad termination");
|
DEBUG_ASSERT(starts_with(p, end_str), detail::assert_handler{}, "bad termination");
|
||||||
p.skip();
|
p.skip();
|
||||||
|
skip(p, " /* clang -E -dI */");
|
||||||
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
|
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
|
||||||
// don't skip newline
|
// don't skip newline
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ bool diagnostic_logger::log(const char* source, const diagnostic& d) const
|
||||||
|
|
||||||
bool stderr_diagnostic_logger::do_log(const char* source, const diagnostic& d) const
|
bool stderr_diagnostic_logger::do_log(const char* source, const diagnostic& d) const
|
||||||
{
|
{
|
||||||
std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity),
|
auto loc = d.location.to_string();
|
||||||
d.location.to_string().c_str(), d.message.c_str());
|
if (loc.empty())
|
||||||
|
std::fprintf(stderr, "[%s] [%s] %s\n", source, to_string(d.severity), d.message.c_str());
|
||||||
|
else
|
||||||
|
std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity),
|
||||||
|
d.location.to_string().c_str(), d.message.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,21 +77,22 @@ namespace ns2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requires clang 4.0, currently not available for testing
|
// requires clang 4.0
|
||||||
// TODO:
|
TEST_CASE("cpp_include_directive", "[!hide][clang4]")
|
||||||
#if 0
|
|
||||||
TEST_CASE("cpp_include_directive")
|
|
||||||
{
|
{
|
||||||
write_file("cpp_include_directive-header.hpp", R"(
|
write_file("cpp_include_directive-header.hpp", R"(
|
||||||
#define FOO
|
#define FOO
|
||||||
)");
|
)");
|
||||||
|
|
||||||
auto header_a = R"(
|
auto header_a = R"(
|
||||||
|
/// #include <iostream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
/// #include "cpp_include_directive-header.hpp"
|
||||||
#include "cpp_include_directive-header.hpp"
|
#include "cpp_include_directive-header.hpp"
|
||||||
)";
|
)";
|
||||||
|
|
||||||
auto header_b = R"(
|
auto header_b = R"(
|
||||||
|
/// #include "header_a.hpp"
|
||||||
#include "header_a.hpp"
|
#include "header_a.hpp"
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
|
@ -105,20 +106,32 @@ TEST_CASE("cpp_include_directive")
|
||||||
{
|
{
|
||||||
REQUIRE(include.target().name() == include.name());
|
REQUIRE(include.target().name() == include.name());
|
||||||
REQUIRE(include.include_kind() == cppast::cpp_include_kind::system);
|
REQUIRE(include.include_kind() == cppast::cpp_include_kind::system);
|
||||||
REQUIRE(!include.target().get(idx));
|
REQUIRE(include.target().get(idx).empty());
|
||||||
}
|
}
|
||||||
else if (include.name() == "cpp_include_directive-header.hpp")
|
else if (include.name() == "cpp_include_directive-header.hpp")
|
||||||
{
|
{
|
||||||
REQUIRE(include.target().name() == include.name());
|
REQUIRE(include.target().name() == include.name());
|
||||||
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
|
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
|
||||||
REQUIRE(!include.target().get(idx));
|
REQUIRE(include.target().get(idx).empty());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
REQUIRE(false);
|
REQUIRE(false);
|
||||||
});
|
});
|
||||||
REQUIRE(count == 2u);
|
REQUIRE(count == 2u);
|
||||||
|
|
||||||
|
count = test_visit<cpp_include_directive>(*file_b, [&](const cpp_include_directive& include) {
|
||||||
|
if (include.name() == "header_a.hpp")
|
||||||
|
{
|
||||||
|
REQUIRE(include.target().name() == include.name());
|
||||||
|
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
|
||||||
|
REQUIRE(
|
||||||
|
equal_ref(idx, include.target(), cpp_file_ref(cpp_entity_id(""), "header_a.hpp")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
REQUIRE(false);
|
||||||
|
});
|
||||||
|
REQUIRE(count == 1u);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST_CASE("comment matching")
|
TEST_CASE("comment matching")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue