From be2ea9eb44b34d91e1fe1b0eef6551f82899c69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 7 Oct 2017 09:24:18 +0200 Subject: [PATCH 1/6] Hide "too many errors" error message --- src/libclang/libclang_parser.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index daba8b5..fa3e2a6 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -328,9 +328,7 @@ libclang_parser::libclang_parser(type_safe::object_ref { } -libclang_parser::~libclang_parser() noexcept -{ -} +libclang_parser::~libclang_parser() noexcept {} namespace { @@ -375,8 +373,8 @@ namespace { auto loc = source_location::make_file(path); // line number won't help auto text = detail::cxstring(clang_getDiagnosticSpelling(diag)); - - logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()}); + if (text != "too many errors emitted, stopping now") + logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()}); } } } From 08e92c873cdaadd7cbe0e32deb72c7806987943c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 7 Oct 2017 09:29:20 +0200 Subject: [PATCH 2/6] Show line number in error message --- src/libclang/libclang_parser.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index fa3e2a6..08628a6 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -371,7 +371,11 @@ namespace auto sev = get_severity(diag); if (sev) { - auto loc = source_location::make_file(path); // line number won't help + auto diag_loc = clang_getDiagnosticLocation(diag); + unsigned line; + clang_getPresumedLocation(diag_loc, nullptr, &line, nullptr); + + 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()}); From 1b468ea4763f2fda265b3cbe5eca380f5aa0a310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 7 Oct 2017 09:35:31 +0200 Subject: [PATCH 3/6] Dump preprocessed file in verbose mode --- include/cppast/libclang_parser.hpp | 14 +++++++++++--- src/libclang/libclang_parser.cpp | 18 ++++++++++++++++-- tool/main.cpp | 11 +++++------ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/cppast/libclang_parser.hpp b/include/cppast/libclang_parser.hpp index 6ff4b48..5b02037 100644 --- a/include/cppast/libclang_parser.hpp +++ b/include/cppast/libclang_parser.hpp @@ -23,6 +23,8 @@ namespace cppast static int clang_version(const libclang_compile_config& config); static const std::vector& 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; }; diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index 08628a6..4fcae1e 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -35,6 +36,12 @@ const std::vector& 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::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; @@ -446,8 +453,15 @@ std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, "config has mismatched type"); auto& config = static_cast(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()); diff --git a/tool/main.cpp b/tool/main.cpp index 9affd3d..8a1ebbb 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -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()); } + if (options.count("verbose")) + config.write_preprocessed(true); + if (options.count("include_directory")) for (auto& include : options["include_directory"].as>()) config.add_include_dir(include); From b3885bbb687e44b7c5de8155a990114f384cf257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sat, 7 Oct 2017 09:58:14 +0200 Subject: [PATCH 4/6] Remove comments in macro invocations Just breaks to much. --- src/libclang/preprocessor.cpp | 6 +++--- test/cpp_preprocessor.cpp | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index 9b40e49..45c3d52 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -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() != '<') diff --git a/test/cpp_preprocessor.cpp b/test/cpp_preprocessor.cpp index a96188f..d2db720 100644 --- a/test/cpp_preprocessor.cpp +++ b/test/cpp_preprocessor.cpp @@ -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 -void k(); +void j(); )"; auto file = parse({}, "comment-matching.cpp", code); From 49d77c5543c61470879a4aa39c1570ff5204ef9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Mon, 9 Oct 2017 09:05:20 +0200 Subject: [PATCH 5/6] Fix issue with asymmetric common parents of friend function and friend --- include/cppast/cpp_forward_declarable.hpp | 2 +- src/libclang/function_parser.cpp | 74 +++++++++++++++++++---- test/cpp_friend.cpp | 26 +++++++- 3 files changed, 86 insertions(+), 16 deletions(-) diff --git a/include/cppast/cpp_forward_declarable.hpp b/include/cppast/cpp_forward_declarable.hpp index b476f5b..eb8c4e1 100644 --- a/include/cppast/cpp_forward_declarable.hpp +++ b/include/cppast/cpp_forward_declarable.hpp @@ -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 diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index b0a4bd4..c3b6163 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -100,6 +100,15 @@ namespace detail::skip_brackets(stream); } + std::vector get_semantic_parents(CXCursor cur) + { + std::vector 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 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 diff --git a/test/cpp_friend.cpp b/test/cpp_friend.cpp index e435e0b..7f4fbc0 100644 --- a/test/cpp_friend.cpp +++ b/test/cpp_friend.cpp @@ -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); } From 8e6c5ccf712da6b3bf5286ee411445e3b87c50d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Mon, 9 Oct 2017 09:20:35 +0200 Subject: [PATCH 6/6] Fix type parser for types with keyword prefix E.g. a type `constant` would become `const ant`. --- src/libclang/type_parser.cpp | 67 +++++++++++++++++++++++++----------- test/code_generator.cpp | 8 +++++ 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 32ae2ec..827d8ed 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -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 { - 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))); diff --git a/test/code_generator.cpp b/test/code_generator.cpp index ae908ab..d4e6dd5 100644 --- a/test/code_generator.cpp +++ b/test/code_generator.cpp @@ -19,6 +19,10 @@ templateclass... 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;