Keep macro definitions when preprocessing

Other includes libclang encounters might depend on them.
This commit is contained in:
Jonathan Müller 2017-04-14 22:38:25 +02:00
commit f359885e87

View file

@ -17,6 +17,8 @@
#include <cppast/cpp_entity_kind.hpp> #include <cppast/cpp_entity_kind.hpp>
#include <cppast/diagnostic.hpp> #include <cppast/diagnostic.hpp>
#include "parse_error.hpp"
using namespace cppast; using namespace cppast;
namespace ts = type_safe; namespace ts = type_safe;
@ -415,10 +417,13 @@ namespace
return true; return true;
} }
void skip_spaces(position& p) void skip_spaces(position& p, bool bump = false)
{ {
while (starts_with(p, " ")) while (starts_with(p, " "))
p.skip(); if (bump)
p.bump();
else
p.skip();
} }
std::unique_ptr<cpp_macro_definition> parse_macro(position& p, std::unique_ptr<cpp_macro_definition> parse_macro(position& p,
@ -427,31 +432,34 @@ namespace
{ {
// format (at new line): #define <name> [replacement] // format (at new line): #define <name> [replacement]
// or: #define <name>(<args>) [replacement] // or: #define <name>(<args>) [replacement]
// note: keep macro definition in file
if (!p.was_newl() || !starts_with(p, "#define")) if (!p.was_newl() || !starts_with(p, "#define"))
return nullptr; return nullptr;
p.skip(std::strlen("#define")); // read line here for comment matching
skip_spaces(p); auto cur_line = p.cur_line();
p.bump(std::strlen("#define"));
skip_spaces(p, true);
std::string name; std::string name;
while (!starts_with(p, "(") && !starts_with(p, " ") && !starts_with(p, "\n")) while (!starts_with(p, "(") && !starts_with(p, " ") && !starts_with(p, "\n"))
{ {
name += *p.ptr(); name += *p.ptr();
p.skip(); p.bump();
} }
ts::optional<std::string> args; ts::optional<std::string> args;
if (starts_with(p, "(")) if (starts_with(p, "("))
{ {
std::string str; std::string str;
for (p.skip(); !starts_with(p, ")"); p.skip()) for (p.bump(); !starts_with(p, ")"); p.bump())
str += *p.ptr(); str += *p.ptr();
p.skip(); p.bump();
args = std::move(str); args = std::move(str);
} }
std::string rep; std::string rep;
auto in_c_comment = false; auto in_c_comment = false;
for (skip_spaces(p); in_c_comment || !starts_with(p, "\n"); p.skip()) for (skip_spaces(p, true); in_c_comment || !starts_with(p, "\n"); p.bump())
{ {
if (starts_with(p, "/*")) if (starts_with(p, "/*"))
in_c_comment = true; in_c_comment = true;
@ -466,7 +474,7 @@ namespace
auto result = cpp_macro_definition::build(std::move(name), std::move(args), std::move(rep)); auto result = cpp_macro_definition::build(std::move(name), std::move(args), std::move(rep));
// match comment directly // match comment directly
if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line())) if (!output.comments.empty() && output.comments.back().matches(*result, cur_line))
{ {
result->set_comment(std::move(output.comments.back().comment)); result->set_comment(std::move(output.comments.back().comment));
output.comments.pop_back(); output.comments.pop_back();
@ -483,10 +491,10 @@ namespace
// however, this kind of usage should be rare™ // however, this kind of usage should be rare™
if (/*!p.was_newl() ||*/ !starts_with(p, "#undef")) if (/*!p.was_newl() ||*/ !starts_with(p, "#undef"))
return ts::nullopt; return ts::nullopt;
p.skip(std::strlen("#undef")); p.bump(std::strlen("#undef"));
std::string result; std::string result;
for (skip_spaces(p); !starts_with(p, "\n"); p.skip()) for (skip_spaces(p, true); !starts_with(p, "\n"); p.bump())
result += *p.ptr(); result += *p.ptr();
// don't skip newline // don't skip newline
@ -499,6 +507,7 @@ namespace
{ {
// format (at new line, literal <>): #include <filename> // format (at new line, literal <>): #include <filename>
// or: #include "filename" // or: #include "filename"
// note: don't write include back
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"));
@ -544,14 +553,14 @@ namespace
return result; return result;
} }
bool skip_pragma(position& p) bool bump_pragma(position& p)
{ {
// format (at new line): #pragma <stuff..>\n // format (at new line): #pragma <stuff..>\n
if (!p.was_newl() || !starts_with(p, "#pragma")) if (!p.was_newl() || !starts_with(p, "#pragma"))
return false; return false;
while (!starts_with(p, "\n")) while (!starts_with(p, "\n"))
p.skip(); p.bump();
// don't skip newline // don't skip newline
return true; return true;
@ -645,11 +654,27 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
{ {
if (auto macro = parse_macro(p, result, file_depth == 0u)) if (auto macro = parse_macro(p, result, file_depth == 0u))
{ {
if (logger.is_verbose())
{
auto message = detail::format("parsing macro '", macro->name(), "'");
logger.log("preprocessor",
diagnostic{std::move(message), source_location::make_file(path),
severity::debug});
}
result.entities.push_back({std::move(macro), p.cur_line()}); result.entities.push_back({std::move(macro), p.cur_line()});
} }
else if (auto undef = parse_undef(p)) else if (auto undef = parse_undef(p))
{ {
if (file_depth == 0u) if (file_depth == 0u)
{
if (logger.is_verbose())
{
auto message = detail::format("undefining macro '", undef.value(), "'");
logger.log("preprocessor",
diagnostic{std::move(message), source_location::make_file(path),
severity::debug});
}
result.entities result.entities
.erase(std::remove_if(result.entities.begin(), result.entities.end(), .erase(std::remove_if(result.entities.begin(), result.entities.end(),
[&](const pp_entity& e) { [&](const pp_entity& e) {
@ -658,13 +683,20 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
&& e.entity->name() == undef.value(); && e.entity->name() == undef.value();
}), }),
result.entities.end()); result.entities.end());
}
} }
else if (auto include = parse_include(p, result, file_depth == 0u)) else if (auto include = parse_include(p, result, file_depth == 0u))
{ {
if (file_depth == 0u) if (logger.is_verbose())
result.entities.push_back({std::move(include), p.cur_line()}); {
auto message = detail::format("parsing include '", include->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()});
} }
else if (skip_pragma(p)) else if (bump_pragma(p))
continue; continue;
else if (auto lm = parse_linemarker(p)) else if (auto lm = parse_linemarker(p))
{ {