Fix issues with fast preprocessing option
This commit is contained in:
commit
d2dc8907e2
3 changed files with 154 additions and 7 deletions
|
|
@ -230,7 +230,114 @@ namespace
|
||||||
return "standardese-macro-file-" + std::to_string(++counter) + ".delete-me";
|
return "standardese-macro-file-" + std::to_string(++counter) + ".delete-me";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string write_macro_file(const libclang_compile_config& c, const char* full_path,
|
template <std::size_t N>
|
||||||
|
void bump_until(std::istreambuf_iterator<char>& iter, const char (&str)[N])
|
||||||
|
{
|
||||||
|
auto ptr = &str[0];
|
||||||
|
while (ptr != &str[N - 1])
|
||||||
|
{
|
||||||
|
if (iter == std::istreambuf_iterator<char>{})
|
||||||
|
// end of file
|
||||||
|
break;
|
||||||
|
else if (*iter != *ptr)
|
||||||
|
{
|
||||||
|
// try again
|
||||||
|
ptr = &str[0];
|
||||||
|
if (*iter == *ptr)
|
||||||
|
++ptr; // it was the first character again
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// okay, move forward
|
||||||
|
++ptr;
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
void skip_whitespace(Iter& begin, Iter end)
|
||||||
|
{
|
||||||
|
while (begin != end && (*begin == ' ' || *begin == '\t'))
|
||||||
|
++begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Iter>
|
||||||
|
std::string get_line(Iter& begin, Iter end)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while (begin != end && *begin != '\n')
|
||||||
|
line += *begin++;
|
||||||
|
++begin; // newline
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::optional<std::string> get_include_guard_macro(const std::string& full_path)
|
||||||
|
{
|
||||||
|
std::ifstream file(full_path);
|
||||||
|
|
||||||
|
auto iter = std::istreambuf_iterator<char>(file);
|
||||||
|
while (iter != std::istreambuf_iterator<char>{})
|
||||||
|
{
|
||||||
|
if (*iter == '/')
|
||||||
|
{
|
||||||
|
++iter;
|
||||||
|
if (*iter == '/')
|
||||||
|
// C++ style comment, bump until \n
|
||||||
|
bump_until(iter, "\n");
|
||||||
|
else if (*iter == '*')
|
||||||
|
// C style comment
|
||||||
|
bump_until(iter, "*/");
|
||||||
|
}
|
||||||
|
else if (*iter == ' ' || *iter == '\t' || *iter == '\n')
|
||||||
|
++iter; // empty
|
||||||
|
else if (*iter == '#')
|
||||||
|
{
|
||||||
|
// preprocessor line
|
||||||
|
auto if_line = get_line(iter, {});
|
||||||
|
if (if_line.compare(0, 3, "#if") != 0)
|
||||||
|
// not something starting with #if
|
||||||
|
break;
|
||||||
|
|
||||||
|
skip_whitespace(iter, {});
|
||||||
|
|
||||||
|
auto macro_line = get_line(iter, {});
|
||||||
|
if (macro_line.compare(0, 7, "#define") != 0)
|
||||||
|
// not a corresponding define
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto macro_name_begin = std::next(macro_line.begin(), 7);
|
||||||
|
// skip whitespace after define
|
||||||
|
skip_whitespace(macro_name_begin, macro_line.end());
|
||||||
|
|
||||||
|
auto macro_name_end = macro_name_begin;
|
||||||
|
// skip over identifier
|
||||||
|
while (macro_name_end != macro_line.end()
|
||||||
|
&& (*macro_name_end == '_' || std::isalnum(*macro_name_end)))
|
||||||
|
++macro_name_end;
|
||||||
|
|
||||||
|
auto trailing_ws = macro_line.rbegin();
|
||||||
|
skip_whitespace(trailing_ws, macro_line.rend());
|
||||||
|
if (macro_name_end != trailing_ws.base())
|
||||||
|
// anything else after macro
|
||||||
|
break;
|
||||||
|
|
||||||
|
std::string macro_name(macro_name_begin, macro_name_end);
|
||||||
|
if (if_line.find(macro_name) == std::string::npos)
|
||||||
|
// macro name doesn't occur in if line
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
return macro_name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// line is neither empty, comment, nor preprocessor
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume no include guard followed a bad line
|
||||||
|
return type_safe::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string write_macro_file(const libclang_compile_config& c, const std::string& full_path,
|
||||||
const diagnostic_logger& logger)
|
const diagnostic_logger& logger)
|
||||||
{
|
{
|
||||||
std::string diagnostic;
|
std::string diagnostic;
|
||||||
|
|
@ -252,13 +359,17 @@ namespace
|
||||||
auto file = get_macro_file_name();
|
auto file = get_macro_file_name();
|
||||||
std::ofstream stream(file);
|
std::ofstream stream(file);
|
||||||
|
|
||||||
auto cmd = get_macro_command(c, full_path);
|
auto cmd = get_macro_command(c, full_path.c_str());
|
||||||
tpl::Process process(cmd, "",
|
tpl::Process process(cmd, "",
|
||||||
[&](const char* str, std::size_t n) {
|
[&](const char* str, std::size_t n) {
|
||||||
stream.write(str, std::streamsize(n));
|
stream.write(str, std::streamsize(n));
|
||||||
},
|
},
|
||||||
diagnostic_logger);
|
diagnostic_logger);
|
||||||
|
|
||||||
|
if (auto include_guard = get_include_guard_macro(full_path))
|
||||||
|
// undefine include guard
|
||||||
|
stream << "#undef " << include_guard.value();
|
||||||
|
|
||||||
auto exit_code = process.get_exit_status();
|
auto exit_code = process.get_exit_status();
|
||||||
DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{});
|
DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{});
|
||||||
if (exit_code != 0)
|
if (exit_code != 0)
|
||||||
|
|
@ -275,7 +386,8 @@ namespace
|
||||||
};
|
};
|
||||||
|
|
||||||
clang_preprocess_result clang_preprocess_impl(const libclang_compile_config& c,
|
clang_preprocess_result clang_preprocess_impl(const libclang_compile_config& c,
|
||||||
const char* full_path, const char* macro_path)
|
const std::string& full_path,
|
||||||
|
const char* macro_path)
|
||||||
{
|
{
|
||||||
clang_preprocess_result result;
|
clang_preprocess_result result;
|
||||||
|
|
||||||
|
|
@ -299,7 +411,7 @@ namespace
|
||||||
diagnostic.push_back(*str);
|
diagnostic.push_back(*str);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto cmd = get_preprocess_command(c, full_path, macro_path);
|
auto cmd = get_preprocess_command(c, full_path.c_str(), macro_path);
|
||||||
tpl::Process process(cmd, "",
|
tpl::Process process(cmd, "",
|
||||||
[&](const char* str, std::size_t n) {
|
[&](const char* str, std::size_t n) {
|
||||||
result.file.reserve(result.file.size() + n);
|
result.file.reserve(result.file.size() + n);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,38 @@ TEST_CASE("preprocessor_parses_escaped_character", "[!hide][clang4]")
|
||||||
libclang_compile_config config;
|
libclang_compile_config config;
|
||||||
config.set_flags(cpp_standard::cpp_latest);
|
config.set_flags(cpp_standard::cpp_latest);
|
||||||
|
|
||||||
auto&& preprocessed = detail::preprocess(config, "ppec.cpp", default_logger().get());
|
auto preprocessed = detail::preprocess(config, "ppec.cpp", default_logger().get());
|
||||||
REQUIRE(preprocessed.includes.size() == 1);
|
REQUIRE(preprocessed.includes.size() == 1);
|
||||||
REQUIRE(preprocessed.includes[0].file_name == "ppec.hpp");
|
REQUIRE(preprocessed.includes[0].file_name == "ppec.hpp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("fast_preprocessing include guard")
|
||||||
|
{
|
||||||
|
auto file_name = "fast_preprocessing_include_guard";
|
||||||
|
write_file(file_name, R"(
|
||||||
|
// This is a C++ comment that should get skipped.
|
||||||
|
|
||||||
|
|
||||||
|
/// This as well.
|
||||||
|
|
||||||
|
/* C style comments
|
||||||
|
|
||||||
|
#ifndef FALSE_POSITIVE
|
||||||
|
#define FALSE_POSITIVE
|
||||||
|
|
||||||
|
*/ #ifndef INCLUDE_GUARD // the include guard macro
|
||||||
|
#define INCLUDE_GUARD
|
||||||
|
|
||||||
|
struct foo {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
)");
|
||||||
|
|
||||||
|
libclang_compile_config config;
|
||||||
|
config.set_flags(cpp_standard::cpp_latest);
|
||||||
|
config.fast_preprocessing(true);
|
||||||
|
|
||||||
|
auto result = detail::preprocess(config, file_name, default_logger().get());
|
||||||
|
REQUIRE(result.macros.size() == 1u);
|
||||||
|
REQUIRE(result.macros[0].macro->name() == "INCLUDE_GUARD");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,8 @@ int main(int argc, char* argv[]) try
|
||||||
cxxopts::value<std::vector<std::string>>())
|
cxxopts::value<std::vector<std::string>>())
|
||||||
("gnu_extensions", "enable GNU extensions (equivalent to -std=gnu++XX)")
|
("gnu_extensions", "enable GNU extensions (equivalent to -std=gnu++XX)")
|
||||||
("msvc_extensions", "enable MSVC extensions (equivalent to -fms-extensions)")
|
("msvc_extensions", "enable MSVC extensions (equivalent to -fms-extensions)")
|
||||||
("msvc_compatibility", "enable MSVC compatibility (equivalent to -fms-compatibility)");
|
("msvc_compatibility", "enable MSVC compatibility (equivalent to -fms-compatibility)")
|
||||||
|
("fast_preprocessing", "enable fast preprocessing, be careful, this breaks if you e.g. redefine macros in the same file!");
|
||||||
// clang-format on
|
// clang-format on
|
||||||
options.parse_positional("file");
|
options.parse_positional("file");
|
||||||
options.parse(argc, argv);
|
options.parse(argc, argv);
|
||||||
|
|
@ -219,7 +220,7 @@ int main(int argc, char* argv[]) try
|
||||||
else if (options.count("version"))
|
else if (options.count("version"))
|
||||||
{
|
{
|
||||||
std::cout << "cppast version " << CPPAST_VERSION_STRING << "\n";
|
std::cout << "cppast version " << CPPAST_VERSION_STRING << "\n";
|
||||||
std::cout << "Copyright (C) Jonathan Müller 2017 <jonathanmueller.dev@gmail.com>\n";
|
std::cout << "Copyright (C) Jonathan Müller 2017-2018 <jonathanmueller.dev@gmail.com>\n";
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
std::cout << "Using libclang version " << CPPAST_CLANG_VERSION_STRING << '\n';
|
std::cout << "Using libclang version " << CPPAST_CLANG_VERSION_STRING << '\n';
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +249,9 @@ int main(int argc, char* argv[]) try
|
||||||
if (options.count("verbose"))
|
if (options.count("verbose"))
|
||||||
config.write_preprocessed(true);
|
config.write_preprocessed(true);
|
||||||
|
|
||||||
|
if (options.count("fast_preprocessing"))
|
||||||
|
config.fast_preprocessing(true);
|
||||||
|
|
||||||
if (options.count("include_directory"))
|
if (options.count("include_directory"))
|
||||||
for (auto& include : options["include_directory"].as<std::vector<std::string>>())
|
for (auto& include : options["include_directory"].as<std::vector<std::string>>())
|
||||||
config.add_include_dir(include);
|
config.add_include_dir(include);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue