Store full path of header as well

This commit is contained in:
Jonathan Müller 2017-06-11 22:16:56 +02:00
commit fd449dde4a
6 changed files with 169 additions and 131 deletions

View file

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

View file

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

View file

@ -104,6 +104,11 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
return entity; return entity;
break; break;
case CXCursor_MacroDefinition:
case CXCursor_InclusionDirective:
DEBUG_UNREACHABLE(detail::assert_handler{}, "handle preprocessor in parser callback");
break;
case CXCursor_Namespace: case CXCursor_Namespace:
return parse_cpp_namespace(context, cur); return parse_cpp_namespace(context, cur);
case CXCursor_NamespaceAlias: case CXCursor_NamespaceAlias:

View file

@ -268,6 +268,89 @@ namespace
p.skip(std::strlen(str)); 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 parse_c_doc_comment(position& p)
{ {
detail::pp_doc_comment result; detail::pp_doc_comment result;
@ -423,15 +506,6 @@ namespace
return true; 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, std::unique_ptr<cpp_macro_definition> parse_macro(position& p,
detail::preprocessor_output& output, detail::preprocessor_output& output,
bool in_main_file) bool in_main_file)
@ -504,15 +578,13 @@ namespace
return result; return result;
} }
std::unique_ptr<cpp_include_directive> parse_include(position& p, type_safe::optional<detail::pp_include> parse_include(position& p, bool in_main_file)
detail::preprocessor_output& output,
bool in_main_file)
{ {
// 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 // 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 type_safe::nullopt;
p.skip(std::strlen("#include")); p.skip(std::strlen("#include"));
if (starts_with(p, "_next")) if (starts_with(p, "_next"))
p.skip(std::strlen("_next")); p.skip(std::strlen("_next"));
@ -544,16 +616,10 @@ namespace
// don't skip newline // don't skip newline
if (!in_main_file) if (!in_main_file)
return nullptr; return type_safe::nullopt;
auto result = cpp_include_directive::build(cpp_file_ref(cpp_entity_id(filename), filename), return detail::pp_include{cpp_file_ref(cpp_entity_id(filename), filename), include_kind,
include_kind); p.cur_line()};
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;
} }
bool bump_pragma(position& p) bool bump_pragma(position& p)
@ -568,80 +634,6 @@ namespace
return true; 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, detail::preprocessor_output detail::preprocess(const libclang_compile_config& config,
@ -682,7 +674,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
severity::debug}); 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)) else if (auto undef = parse_undef(p))
{ {
@ -695,26 +687,24 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
diagnostic{std::move(message), source_location::make_file(path), diagnostic{std::move(message), source_location::make_file(path),
severity::debug}); severity::debug});
} }
result.entities result.macros.erase(std::remove_if(result.macros.begin(), result.macros.end(),
.erase(std::remove_if(result.entities.begin(), result.entities.end(), [&](const pp_macro& e) {
[&](const pp_entity& e) { return e.macro->name() == undef.value();
return e.entity->kind() }),
== cpp_entity_kind::macro_definition_t result.macros.end());
&& e.entity->name() == undef.value();
}),
result.entities.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()) 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", logger.log("preprocessor",
diagnostic{std::move(message), source_location::make_file(path), diagnostic{std::move(message), source_location::make_file(path),
severity::debug}); 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)) else if (bump_pragma(p))
continue; continue;

View file

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

View file

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