Fix a couple of bugs
This commit is contained in:
commit
5c5f30aa7a
10 changed files with 189 additions and 66 deletions
|
|
@ -49,7 +49,7 @@ namespace cppast
|
|||
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.
|
||||
/// \notes This may include template parameters.
|
||||
std::string semantic_scope() const noexcept
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ namespace cppast
|
|||
static int clang_version(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,
|
||||
|
|
@ -34,9 +36,7 @@ namespace cppast
|
|||
{
|
||||
public:
|
||||
/// \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.
|
||||
|
|
@ -121,6 +121,13 @@ namespace cppast
|
|||
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:
|
||||
void do_set_flags(cpp_standard standard, compile_flags flags) override;
|
||||
|
||||
|
|
@ -137,6 +144,7 @@ namespace cppast
|
|||
|
||||
std::string clang_binary_;
|
||||
int clang_version_;
|
||||
bool write_preprocessed_;
|
||||
|
||||
friend detail::libclang_compile_config_access;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -100,6 +100,15 @@ namespace
|
|||
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)
|
||||
{
|
||||
auto kind = clang_getCursorKind(parent);
|
||||
|
|
@ -108,6 +117,9 @@ namespace
|
|||
|| 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)
|
||||
{
|
||||
auto parent = clang_getCursorLexicalParent(cur);
|
||||
|
|
@ -138,25 +150,63 @@ namespace
|
|||
|
||||
type_safe::optional<cpp_entity_ref> parse_scope(const CXCursor& cur, bool is_friend)
|
||||
{
|
||||
std::string scope;
|
||||
// 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
|
||||
for (auto definition = get_definition_scope(cur, is_friend),
|
||||
parent = clang_getCursorSemanticParent(cur);
|
||||
!equivalent_cursor(definition, parent); parent = clang_getCursorSemanticParent(parent))
|
||||
std::string scope_name;
|
||||
|
||||
auto friended = clang_getCursorReferenced(cur);
|
||||
if (is_friend && !clang_Cursor_isNull(friended))
|
||||
{
|
||||
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,
|
||||
"infinite loop while calculating scope");
|
||||
auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent));
|
||||
scope = parent_name.std_str() + "::" + std::move(scope);
|
||||
"invalid common parent of friend and friended");
|
||||
|
||||
// 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;
|
||||
else
|
||||
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
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cppast/libclang_parser.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <clang-c/CXCompilationDatabase.h>
|
||||
|
|
@ -35,6 +36,12 @@ const std::vector<std::string>& detail::libclang_compile_config_access::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)
|
||||
{
|
||||
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
|
||||
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
|
||||
{
|
||||
|
|
@ -373,10 +378,14 @@ namespace
|
|||
auto sev = get_severity(diag);
|
||||
if (sev)
|
||||
{
|
||||
auto loc = source_location::make_file(path); // line number won't help
|
||||
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
|
||||
auto diag_loc = clang_getDiagnosticLocation(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");
|
||||
auto& config = static_cast<const libclang_compile_config&>(c);
|
||||
|
||||
// preprocess + parse
|
||||
// preprocess
|
||||
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 file = clang_getFile(tu.get(), path.c_str());
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ namespace
|
|||
// -x c++: force C++ as input language
|
||||
// -I.: add current working directory to include search path
|
||||
// -E: print preprocessor output
|
||||
// -CC: keep comments, even in macro
|
||||
// -C: keep comments
|
||||
// -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)
|
||||
// -Xclang -dI: print include directives as well (clang >= 4.0.0)
|
||||
flags += " -Xclang -dI";
|
||||
|
|
@ -735,7 +735,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
|
|||
{
|
||||
case linemarker::line_directive:
|
||||
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:
|
||||
if (file_depth == 0u && lm.value().file.front() != '<')
|
||||
|
|
|
|||
|
|
@ -33,10 +33,23 @@ namespace
|
|||
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);
|
||||
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);
|
||||
remove_trailing_ws(str);
|
||||
|
|
@ -46,10 +59,23 @@ namespace
|
|||
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);
|
||||
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);
|
||||
remove_leading_ws(str);
|
||||
|
|
@ -69,16 +95,16 @@ namespace
|
|||
cpp_cv suffix_cv(std::string& spelling)
|
||||
{
|
||||
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;
|
||||
else
|
||||
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;
|
||||
else
|
||||
cv = cpp_cv_volatile;
|
||||
|
|
@ -88,20 +114,19 @@ namespace
|
|||
}
|
||||
|
||||
// const/volatile at the beginning
|
||||
// (weird that the better version is less performant, isn't it?)
|
||||
cpp_cv prefix_cv(std::string& spelling)
|
||||
{
|
||||
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;
|
||||
else
|
||||
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;
|
||||
else
|
||||
cv = cpp_cv_volatile;
|
||||
|
|
@ -172,9 +197,9 @@ namespace
|
|||
|
||||
// remove struct/class/union prefix on inline type definition
|
||||
// i.e. C's typedef struct idiom
|
||||
remove_prefix(spelling, "struct");
|
||||
remove_prefix(spelling, "class");
|
||||
remove_prefix(spelling, "union");
|
||||
remove_prefix(spelling, "struct", true);
|
||||
remove_prefix(spelling, "class", true);
|
||||
remove_prefix(spelling, "union", true);
|
||||
|
||||
auto entity = b(std::move(spelling));
|
||||
if (!entity)
|
||||
|
|
@ -277,9 +302,9 @@ namespace
|
|||
|
||||
cpp_reference member_function_ref_qualifier(std::string& spelling)
|
||||
{
|
||||
if (remove_suffix(spelling, "&&"))
|
||||
if (remove_suffix(spelling, "&&", false))
|
||||
return cpp_ref_rvalue;
|
||||
else if (remove_suffix(spelling, "&"))
|
||||
else if (remove_suffix(spelling, "&", false))
|
||||
return cpp_ref_lvalue;
|
||||
return cpp_ref_none;
|
||||
}
|
||||
|
|
@ -453,9 +478,9 @@ namespace
|
|||
return nullptr; // don't use decltype here
|
||||
|
||||
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;
|
||||
remove_suffix(spelling, "..."); // variadic decltype. fun
|
||||
remove_suffix(spelling, "...", false); // variadic decltype. fun
|
||||
DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{},
|
||||
type, "unexpected spelling");
|
||||
spelling.pop_back();
|
||||
|
|
@ -652,7 +677,7 @@ namespace
|
|||
case CXType_Typedef:
|
||||
return make_leave_type(type, [&](std::string&& spelling) {
|
||||
auto decl = clang_getTypeDeclaration(type);
|
||||
if (remove_prefix(spelling, "(anonymous"))
|
||||
if (remove_prefix(spelling, "(anonymous", false))
|
||||
spelling = ""; // anonymous type
|
||||
return cpp_user_defined_type::build(
|
||||
cpp_type_ref(detail::get_entity_id(decl), std::move(spelling)));
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ template<template<typename>class... T>
|
|||
struct templated{
|
||||
};
|
||||
|
||||
using struct_=int;
|
||||
|
||||
struct_ var2;
|
||||
|
||||
struct foo{
|
||||
int a;
|
||||
|
||||
|
|
@ -56,6 +60,10 @@ struct templated
|
|||
{
|
||||
};
|
||||
|
||||
using struct_ = int;
|
||||
|
||||
struct_ var2;
|
||||
|
||||
struct foo
|
||||
{
|
||||
int a;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,21 @@ class g {};
|
|||
|
||||
namespace ns
|
||||
{
|
||||
namespace other_ns
|
||||
{
|
||||
namespace inner
|
||||
{
|
||||
/// h2
|
||||
void h2() {}
|
||||
}
|
||||
}
|
||||
|
||||
/// h
|
||||
class h {};
|
||||
class h
|
||||
{
|
||||
/// friend void other_ns::inner::h2();
|
||||
friend void other_ns::inner::h2();
|
||||
};
|
||||
}
|
||||
|
||||
/// b::f
|
||||
|
|
@ -115,7 +128,7 @@ int d() {}
|
|||
)";
|
||||
|
||||
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);
|
||||
REQUIRE(def.has_value());
|
||||
REQUIRE(def.value().comment());
|
||||
|
|
@ -184,6 +197,13 @@ int d() {}
|
|||
REQUIRE(func.definition());
|
||||
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")
|
||||
{
|
||||
REQUIRE(func.semantic_scope() == "b::");
|
||||
|
|
@ -269,5 +289,5 @@ int d() {}
|
|||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 17u);
|
||||
REQUIRE(count == 18u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,23 +173,20 @@ f **/
|
|||
#define g(name) \
|
||||
class name \
|
||||
{ \
|
||||
/** i
|
||||
i */ \
|
||||
void i(); \
|
||||
};
|
||||
|
||||
/// h
|
||||
/// h
|
||||
g(h)
|
||||
|
||||
/// j
|
||||
/// j
|
||||
using j = int;
|
||||
/// i
|
||||
/// i
|
||||
using i = int;
|
||||
|
||||
/// k
|
||||
/// k
|
||||
/// j
|
||||
/// j
|
||||
template <typename T/**/>
|
||||
void k();
|
||||
void j();
|
||||
)";
|
||||
|
||||
auto file = parse({}, "comment-matching.cpp", code);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
void do_indent() override
|
||||
{
|
||||
}
|
||||
void do_unindent() override
|
||||
{
|
||||
}
|
||||
void do_indent() override {}
|
||||
void do_unindent() override {}
|
||||
|
||||
// called when a generic token sequence should be generated
|
||||
// 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>());
|
||||
}
|
||||
|
||||
if (options.count("verbose"))
|
||||
config.write_preprocessed(true);
|
||||
|
||||
if (options.count("include_directory"))
|
||||
for (auto& include : options["include_directory"].as<std::vector<std::string>>())
|
||||
config.add_include_dir(include);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue