Store full path of header as well
This commit is contained in:
parent
6d261cef16
commit
fd449dde4a
6 changed files with 169 additions and 131 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,6 +104,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:
|
||||
|
|
|
|||
|
|
@ -268,6 +268,89 @@ namespace
|
|||
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;
|
||||
|
|
@ -423,15 +506,6 @@ namespace
|
|||
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 +578,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"));
|
||||
|
|
@ -544,16 +616,10 @@ namespace
|
|||
// 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 +634,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,
|
||||
|
|
@ -682,7 +674,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 +687,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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue