Fix a couple of bugs

This commit is contained in:
Jonathan Müller 2017-10-09 09:38:49 +02:00
commit 5c5f30aa7a
10 changed files with 189 additions and 66 deletions

View file

@ -49,7 +49,7 @@ namespace cppast
return semantic_parent_; return semantic_parent_;
} }
/// \returns The name of the semantic parent, if it has own, /// \returns The name of the semantic parent, if it has one,
/// else the empty string. /// else the empty string.
/// \notes This may include template parameters. /// \notes This may include template parameters.
std::string semantic_scope() const noexcept std::string semantic_scope() const noexcept

View file

@ -23,6 +23,8 @@ namespace cppast
static int clang_version(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);
static bool write_preprocessed(const libclang_compile_config& config);
}; };
void for_each_file(const libclang_compilation_database& database, void* user_data, void for_each_file(const libclang_compilation_database& database, void* user_data,
@ -34,9 +36,7 @@ namespace cppast
{ {
public: public:
/// \effects Creates it with a message. /// \effects Creates it with a message.
libclang_error(std::string msg) : std::runtime_error(std::move(msg)) libclang_error(std::string msg) : std::runtime_error(std::move(msg)) {}
{
}
}; };
/// A compilation database. /// A compilation database.
@ -121,6 +121,13 @@ namespace cppast
clang_version_ = major * 10000 + minor * 100 + patch; clang_version_ = major * 10000 + minor * 100 + patch;
} }
/// \effects Sets whether or not the preprocessed file will be written out.
/// Default value is `false`.
void write_preprocessed(bool b) noexcept
{
write_preprocessed_ = b;
}
private: private:
void do_set_flags(cpp_standard standard, compile_flags flags) override; void do_set_flags(cpp_standard standard, compile_flags flags) override;
@ -137,6 +144,7 @@ namespace cppast
std::string clang_binary_; std::string clang_binary_;
int clang_version_; int clang_version_;
bool write_preprocessed_;
friend detail::libclang_compile_config_access; friend detail::libclang_compile_config_access;
}; };

View file

@ -100,6 +100,15 @@ namespace
detail::skip_brackets(stream); detail::skip_brackets(stream);
} }
std::vector<CXCursor> get_semantic_parents(CXCursor cur)
{
std::vector<CXCursor> result;
for (; !clang_isTranslationUnit(clang_getCursorKind(cur));
cur = clang_getCursorSemanticParent(cur))
result.push_back(cur);
return result;
}
bool is_class(const CXCursor& parent) bool is_class(const CXCursor& parent)
{ {
auto kind = clang_getCursorKind(parent); auto kind = clang_getCursorKind(parent);
@ -108,6 +117,9 @@ namespace
|| kind == CXCursor_ClassTemplatePartialSpecialization; || kind == CXCursor_ClassTemplatePartialSpecialization;
} }
// returns the scope where the function is contained in
// for regular functions that is the lexcial parent
// for friend functions it is the enclosing scope of the class
CXCursor get_definition_scope(const CXCursor& cur, bool is_friend) CXCursor get_definition_scope(const CXCursor& cur, bool is_friend)
{ {
auto parent = clang_getCursorLexicalParent(cur); auto parent = clang_getCursorLexicalParent(cur);
@ -138,25 +150,63 @@ namespace
type_safe::optional<cpp_entity_ref> parse_scope(const CXCursor& cur, bool is_friend) type_safe::optional<cpp_entity_ref> parse_scope(const CXCursor& cur, bool is_friend)
{ {
std::string scope; std::string scope_name;
// find the semantic parents we need until we're at the same level of the parent where the definition is
// the semantic parents are all the scopes that need to be appended auto friended = clang_getCursorReferenced(cur);
for (auto definition = get_definition_scope(cur, is_friend), if (is_friend && !clang_Cursor_isNull(friended))
parent = clang_getCursorSemanticParent(cur);
!equivalent_cursor(definition, parent); parent = clang_getCursorSemanticParent(parent))
{ {
DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(parent)), // it refers to another function
// find the common parent between the two cursors
// scope is the scope from the common parent down to the function
auto friended_parents = get_semantic_parents(friended);
auto cur_parents = get_semantic_parents(get_definition_scope(cur, true));
// remove common parents
while (!friended_parents.empty() && !cur_parents.empty()
&& equivalent_cursor(friended_parents.back(), cur_parents.back()))
{
friended_parents.pop_back();
cur_parents.pop_back();
}
DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(friended_parents.back()))
&& !friended_parents.empty(),
detail::parse_error_handler{}, cur, detail::parse_error_handler{}, cur,
"infinite loop while calculating scope"); "invalid common parent of friend and friended");
auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent));
scope = parent_name.std_str() + "::" + std::move(scope); // scope consists of all remaining parents of friended
// (last one is cursor itself)
for (auto iter = friended_parents.rbegin(); iter != std::prev(friended_parents.rend());
++iter)
{
auto parent_name = detail::cxstring(clang_getCursorDisplayName(*iter));
scope_name += parent_name.std_str() + "::";
}
}
else
{
// find the difference between the definition scope parent and semantic parent
// all semantic parents in between form the scope
// the definition scope is the lexical parent for regular functions,
// and the scope outside of the class for friend functions
for (auto definition = get_definition_scope(cur, is_friend),
parent = clang_getCursorSemanticParent(cur);
!equivalent_cursor(definition, parent);
parent = clang_getCursorSemanticParent(parent))
{
DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(parent)),
detail::parse_error_handler{}, cur,
"infinite loop while calculating scope");
auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent));
scope_name = parent_name.std_str() + "::" + std::move(scope_name);
}
} }
if (scope.empty()) if (scope_name.empty())
return type_safe::nullopt; return type_safe::nullopt;
else else
return cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), return cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope)); std::move(scope_name));
} }
// just the tokens occurring in the prefix // just the tokens occurring in the prefix

View file

@ -5,6 +5,7 @@
#include <cppast/libclang_parser.hpp> #include <cppast/libclang_parser.hpp>
#include <cstring> #include <cstring>
#include <fstream>
#include <vector> #include <vector>
#include <clang-c/CXCompilationDatabase.h> #include <clang-c/CXCompilationDatabase.h>
@ -35,6 +36,12 @@ const std::vector<std::string>& detail::libclang_compile_config_access::flags(
return config.get_flags(); return config.get_flags();
} }
bool detail::libclang_compile_config_access::write_preprocessed(
const libclang_compile_config& config)
{
return config.write_preprocessed_;
}
libclang_compilation_database::libclang_compilation_database(const std::string& build_directory) libclang_compilation_database::libclang_compilation_database(const std::string& build_directory)
{ {
static_assert(std::is_same<database, CXCompilationDatabase>::value, "forgot to update type"); static_assert(std::is_same<database, CXCompilationDatabase>::value, "forgot to update type");
@ -74,7 +81,7 @@ namespace
} }
} }
libclang_compile_config::libclang_compile_config() : compile_config({}) libclang_compile_config::libclang_compile_config() : compile_config({}), write_preprocessed_(false)
{ {
// set given clang binary // set given clang binary
auto ptr = CPPAST_CLANG_VERSION_STRING; auto ptr = CPPAST_CLANG_VERSION_STRING;
@ -328,9 +335,7 @@ libclang_parser::libclang_parser(type_safe::object_ref<const diagnostic_logger>
{ {
} }
libclang_parser::~libclang_parser() noexcept libclang_parser::~libclang_parser() noexcept {}
{
}
namespace namespace
{ {
@ -373,10 +378,14 @@ namespace
auto sev = get_severity(diag); auto sev = get_severity(diag);
if (sev) if (sev)
{ {
auto loc = source_location::make_file(path); // line number won't help auto diag_loc = clang_getDiagnosticLocation(diag);
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag)); unsigned line;
clang_getPresumedLocation(diag_loc, nullptr, &line, nullptr);
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()}); auto loc = source_location::make_file(path, line);
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
if (text != "too many errors emitted, stopping now")
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()});
} }
} }
} }
@ -444,8 +453,15 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
"config has mismatched type"); "config has mismatched type");
auto& config = static_cast<const libclang_compile_config&>(c); auto& config = static_cast<const libclang_compile_config&>(c);
// preprocess + parse // preprocess
auto preprocessed = detail::preprocess(config, path.c_str(), logger()); auto preprocessed = detail::preprocess(config, path.c_str(), logger());
if (detail::libclang_compile_config_access::write_preprocessed(config))
{
std::ofstream file(path + ".pp");
file << preprocessed.source;
}
// parse
auto tu = get_cxunit(logger(), pimpl_->index, config, path.c_str(), preprocessed.source); auto tu = get_cxunit(logger(), pimpl_->index, config, path.c_str(), preprocessed.source);
auto file = clang_getFile(tu.get(), path.c_str()); auto file = clang_getFile(tu.get(), path.c_str());

View file

@ -36,9 +36,9 @@ namespace
// -x c++: force C++ as input language // -x c++: force C++ as input language
// -I.: add current working directory to include search path // -I.: add current working directory to include search path
// -E: print preprocessor output // -E: print preprocessor output
// -CC: keep comments, even in macro // -C: keep comments
// -dD: print macro definitions as well // -dD: print macro definitions as well
auto flags = std::string("-x c++ -I. -E -CC -dD"); auto flags = std::string("-x c++ -I. -E -C -dD");
if (detail::libclang_compile_config_access::clang_version(c) >= 40000) if (detail::libclang_compile_config_access::clang_version(c) >= 40000)
// -Xclang -dI: print include directives as well (clang >= 4.0.0) // -Xclang -dI: print include directives as well (clang >= 4.0.0)
flags += " -Xclang -dI"; flags += " -Xclang -dI";
@ -735,7 +735,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
{ {
case linemarker::line_directive: case linemarker::line_directive:
break; // ignore break; // ignore
// no need to handle it, preprocessed output doesn't need to match line numbers precisely // no need to handle it, preprocessed output doesn't need to match line numbers precisely
case linemarker::enter_new: case linemarker::enter_new:
if (file_depth == 0u && lm.value().file.front() != '<') if (file_depth == 0u && lm.value().file.front() != '<')

View file

@ -33,10 +33,23 @@ namespace
str.erase(0, 1); str.erase(0, 1);
} }
bool remove_suffix(std::string& str, const char* suffix) bool can_extend_suffix(const std::string& str, std::size_t suffix_length)
{
if (str.length() <= suffix_length + 1u)
{
auto c = str[str.length() - suffix_length - 1u];
return std::isalnum(c) || c == '_';
}
else
return false;
}
// if identifier only removes maximal suffix according to tokenization
bool remove_suffix(std::string& str, const char* suffix, bool is_identifier)
{ {
auto length = std::strlen(suffix); auto length = std::strlen(suffix);
if (str.length() >= length && str.compare(str.length() - length, length, suffix) == 0) if (str.length() >= length && str.compare(str.length() - length, length, suffix) == 0
&& (!is_identifier || !can_extend_suffix(str, length)))
{ {
str.erase(str.length() - length); str.erase(str.length() - length);
remove_trailing_ws(str); remove_trailing_ws(str);
@ -46,10 +59,23 @@ namespace
return false; return false;
} }
bool remove_prefix(std::string& str, const char* prefix) bool can_extend_prefix(const std::string& str, std::size_t prefix_length)
{
if (prefix_length < str.size())
{
auto c = str[prefix_length];
return std::isalnum(c) || c == '_';
}
else
return false;
}
// if identifier only removes maximal suffix according to tokenization
bool remove_prefix(std::string& str, const char* prefix, bool is_identifier)
{ {
auto length = std::strlen(prefix); auto length = std::strlen(prefix);
if (str.length() >= length && str.compare(0u, length, prefix) == 0) if (str.length() >= length && str.compare(0u, length, prefix) == 0
&& (!is_identifier || !can_extend_prefix(str, length)))
{ {
str.erase(0u, length); str.erase(0u, length);
remove_leading_ws(str); remove_leading_ws(str);
@ -69,16 +95,16 @@ namespace
cpp_cv suffix_cv(std::string& spelling) cpp_cv suffix_cv(std::string& spelling)
{ {
auto cv = cpp_cv_none; auto cv = cpp_cv_none;
if (remove_suffix(spelling, "const")) if (remove_suffix(spelling, "const", true))
{ {
if (remove_suffix(spelling, "volatile")) if (remove_suffix(spelling, "volatile", true))
cv = cpp_cv_const_volatile; cv = cpp_cv_const_volatile;
else else
cv = cpp_cv_const; cv = cpp_cv_const;
} }
else if (remove_suffix(spelling, "volatile")) else if (remove_suffix(spelling, "volatile", true))
{ {
if (remove_suffix(spelling, "const")) if (remove_suffix(spelling, "const", true))
cv = cpp_cv_const_volatile; cv = cpp_cv_const_volatile;
else else
cv = cpp_cv_volatile; cv = cpp_cv_volatile;
@ -88,20 +114,19 @@ namespace
} }
// const/volatile at the beginning // const/volatile at the beginning
// (weird that the better version is less performant, isn't it?)
cpp_cv prefix_cv(std::string& spelling) cpp_cv prefix_cv(std::string& spelling)
{ {
auto cv = cpp_cv_none; auto cv = cpp_cv_none;
if (remove_prefix(spelling, "const")) if (remove_prefix(spelling, "const", true))
{ {
if (remove_prefix(spelling, "volatile")) if (remove_prefix(spelling, "volatile", true))
cv = cpp_cv_const_volatile; cv = cpp_cv_const_volatile;
else else
cv = cpp_cv_const; cv = cpp_cv_const;
} }
else if (remove_prefix(spelling, "volatile")) else if (remove_prefix(spelling, "volatile", true))
{ {
if (remove_prefix(spelling, "const")) if (remove_prefix(spelling, "const", true))
cv = cpp_cv_const_volatile; cv = cpp_cv_const_volatile;
else else
cv = cpp_cv_volatile; cv = cpp_cv_volatile;
@ -172,9 +197,9 @@ namespace
// remove struct/class/union prefix on inline type definition // remove struct/class/union prefix on inline type definition
// i.e. C's typedef struct idiom // i.e. C's typedef struct idiom
remove_prefix(spelling, "struct"); remove_prefix(spelling, "struct", true);
remove_prefix(spelling, "class"); remove_prefix(spelling, "class", true);
remove_prefix(spelling, "union"); remove_prefix(spelling, "union", true);
auto entity = b(std::move(spelling)); auto entity = b(std::move(spelling));
if (!entity) if (!entity)
@ -277,9 +302,9 @@ namespace
cpp_reference member_function_ref_qualifier(std::string& spelling) cpp_reference member_function_ref_qualifier(std::string& spelling)
{ {
if (remove_suffix(spelling, "&&")) if (remove_suffix(spelling, "&&", false))
return cpp_ref_rvalue; return cpp_ref_rvalue;
else if (remove_suffix(spelling, "&")) else if (remove_suffix(spelling, "&", false))
return cpp_ref_lvalue; return cpp_ref_lvalue;
return cpp_ref_none; return cpp_ref_none;
} }
@ -453,9 +478,9 @@ namespace
return nullptr; // don't use decltype here return nullptr; // don't use decltype here
return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr<cpp_type> { return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr<cpp_type> {
if (!remove_prefix(spelling, "decltype(")) if (!remove_prefix(spelling, "decltype(", false))
return nullptr; return nullptr;
remove_suffix(spelling, "..."); // variadic decltype. fun remove_suffix(spelling, "...", false); // variadic decltype. fun
DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{}, DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{},
type, "unexpected spelling"); type, "unexpected spelling");
spelling.pop_back(); spelling.pop_back();
@ -652,7 +677,7 @@ namespace
case CXType_Typedef: case CXType_Typedef:
return make_leave_type(type, [&](std::string&& spelling) { return make_leave_type(type, [&](std::string&& spelling) {
auto decl = clang_getTypeDeclaration(type); auto decl = clang_getTypeDeclaration(type);
if (remove_prefix(spelling, "(anonymous")) if (remove_prefix(spelling, "(anonymous", false))
spelling = ""; // anonymous type spelling = ""; // anonymous type
return cpp_user_defined_type::build( return cpp_user_defined_type::build(
cpp_type_ref(detail::get_entity_id(decl), std::move(spelling))); cpp_type_ref(detail::get_entity_id(decl), std::move(spelling)));

View file

@ -19,6 +19,10 @@ template<template<typename>class... T>
struct templated{ struct templated{
}; };
using struct_=int;
struct_ var2;
struct foo{ struct foo{
int a; int a;
@ -56,6 +60,10 @@ struct templated
{ {
}; };
using struct_ = int;
struct_ var2;
struct foo struct foo
{ {
int a; int a;

View file

@ -36,8 +36,21 @@ class g {};
namespace ns namespace ns
{ {
namespace other_ns
{
namespace inner
{
/// h2
void h2() {}
}
}
/// h /// h
class h {}; class h
{
/// friend void other_ns::inner::h2();
friend void other_ns::inner::h2();
};
} }
/// b::f /// b::f
@ -115,7 +128,7 @@ int d() {}
)"; )";
cpp_entity_index idx; cpp_entity_index idx;
auto check_definition = [&](cpp_entity_id id, const char* name) { auto check_definition = [&](cpp_entity_id id, const char* name) {
auto def = idx.lookup_definition(id); auto def = idx.lookup_definition(id);
REQUIRE(def.has_value()); REQUIRE(def.has_value());
REQUIRE(def.value().comment()); REQUIRE(def.value().comment());
@ -184,6 +197,13 @@ int d() {}
REQUIRE(func.definition()); REQUIRE(func.definition());
check_definition(func.definition().value(), "b::f"); check_definition(func.definition().value(), "b::f");
} }
else if (func.name() == "h2")
{
REQUIRE(func.semantic_scope() == "other_ns::inner::");
REQUIRE(func.is_declaration());
REQUIRE(func.definition());
check_definition(func.definition().value(), "h2");
}
else if (func.name() == "operator int") else if (func.name() == "operator int")
{ {
REQUIRE(func.semantic_scope() == "b::"); REQUIRE(func.semantic_scope() == "b::");
@ -269,5 +289,5 @@ int d() {}
else else
REQUIRE(false); REQUIRE(false);
}); });
REQUIRE(count == 17u); REQUIRE(count == 18u);
} }

View file

@ -173,23 +173,20 @@ f **/
#define g(name) \ #define g(name) \
class name \ class name \
{ \ { \
/** i
i */ \
void i(); \
}; };
/// h /// h
/// h /// h
g(h) g(h)
/// j /// i
/// j /// i
using j = int; using i = int;
/// k /// j
/// k /// j
template <typename T/**/> template <typename T/**/>
void k(); void j();
)"; )";
auto file = parse({}, "comment-matching.cpp", code); auto file = parse({}, "comment-matching.cpp", code);

View file

@ -87,12 +87,8 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e)
} }
// no need to handle indentation, as only a single line is used // no need to handle indentation, as only a single line is used
void do_indent() override void do_indent() override {}
{ void do_unindent() override {}
}
void do_unindent() override
{
}
// called when a generic token sequence should be generated // called when a generic token sequence should be generated
// there are specialized callbacks for various token kinds, // there are specialized callbacks for various token kinds,
@ -249,6 +245,9 @@ int main(int argc, char* argv[]) try
cppast::libclang_compile_config(database, options["file"].as<std::string>()); cppast::libclang_compile_config(database, options["file"].as<std::string>());
} }
if (options.count("verbose"))
config.write_preprocessed(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);