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] 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); }