Fix issue with asymmetric common parents of friend function and friend

This commit is contained in:
Jonathan Müller 2017-10-09 09:05:20 +02:00
commit 49d77c5543
3 changed files with 86 additions and 16 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

@ -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

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