Support C++17 nested namespaces

This commit is contained in:
Jonathan Müller 2017-12-22 17:24:43 +01:00
commit 9e8dc5dd26
10 changed files with 77 additions and 29 deletions

View file

@ -23,9 +23,9 @@ namespace cppast
class builder
{
public:
/// \effects Sets the namespace name and whether it is inline.
explicit builder(std::string name, bool is_inline)
: namespace_(new cpp_namespace(std::move(name), is_inline))
/// \effects Sets the namespace name and whether it is inline and nested.
explicit builder(std::string name, bool is_inline, bool is_nested)
: namespace_(new cpp_namespace(std::move(name), is_inline, is_nested))
{
}
@ -60,6 +60,12 @@ namespace cppast
return inline_;
}
/// \returns Whether or not the namespace is part of a C++17 nested namespace.
bool is_nested() const noexcept
{
return nested_;
}
/// \returns Whether or not the namespace is anonymous.
bool is_anonymous() const noexcept
{
@ -67,8 +73,8 @@ namespace cppast
}
private:
cpp_namespace(std::string name, bool is_inline)
: cpp_entity(std::move(name)), inline_(is_inline)
cpp_namespace(std::string name, bool is_inline, bool is_nested)
: cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested)
{
}
@ -80,6 +86,7 @@ namespace cppast
}
bool inline_;
bool nested_;
};
/// \exclude
@ -184,9 +191,7 @@ namespace cppast
}
private:
cpp_using_declaration(cpp_entity_ref target) : cpp_entity(""), target_(std::move(target))
{
}
cpp_using_declaration(cpp_entity_ref target) : cpp_entity(""), target_(std::move(target)) {}
cpp_entity_kind do_get_entity_kind() const noexcept override;

View file

@ -167,7 +167,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
== CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows
// other children due to templates and stuff
return;
else if (auto entity = parse_entity(context, child))
else if (auto entity = parse_entity(context, &builder.get(), child))
builder.add_child(std::move(entity));
});
}

View file

@ -54,7 +54,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_friend(const detail::parse_context
// for some reason libclang gives a type ref here
// we actually need a class decl cursor, so parse the referenced one
// this might be a definition, so give friend information to the parser
entity = parse_entity(context, referenced, cur);
entity = parse_entity(context, nullptr, referenced, cur);
comment = type_safe::copy(entity->comment()).value_or("");
}
}
@ -74,7 +74,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_friend(const detail::parse_context
}
else if (clang_isDeclaration(kind))
{
entity = parse_entity(context, child, cur);
entity = parse_entity(context, nullptr, child, cur);
if (entity)
{
// steal comment

View file

@ -30,7 +30,7 @@ std::unique_ptr<cpp_entity> detail::try_parse_cpp_language_linkage(const parse_c
auto builder = cpp_language_linkage::builder(name.c_str());
context.comments.match(builder.get(), cur);
detail::visit_children(cur, [&](const CXCursor& child) {
auto entity = parse_entity(context, child);
auto entity = parse_entity(context, &builder.get(), child);
if (entity)
builder.add_child(std::move(entity));
});

View file

@ -504,7 +504,7 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
macro_iter != preprocessed.macros.end() && macro_iter->line <= line; ++macro_iter)
builder.add_child(std::move(macro_iter->macro));
auto entity = detail::parse_entity(context, cur);
auto entity = detail::parse_entity(context, &builder.get(), cur);
if (entity)
builder.add_child(std::move(entity));
}

View file

@ -18,42 +18,63 @@ namespace
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [inline] namespace [<attribute>] <identifier> {
// [inline] namespace|:: [<attribute>] <identifier> [{]
auto is_inline = false;
if (skip_if(stream, "inline"))
is_inline = true;
skip(stream, "namespace");
// C++17 nested namespace declarations get one cursor per nested name.
// The first cursor starts with the `namespace` keyword, and the
// following start with the `::` separator. Either way, it is skipped.
auto is_nested = false;
if (!detail::skip_if(stream, "namespace"))
{
is_nested = true;
skip(stream, "::");
}
auto attributes = parse_attributes(stream);
// <identifier> {
// or when anonymous: {
if (detail::skip_if(stream, "{"))
return cpp_namespace::builder("", is_inline);
return cpp_namespace::builder("", is_inline, false);
auto& name = stream.get().value();
auto other_attributes = parse_attributes(stream);
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
skip(stream, "{");
// If the next token is not `::`, there are no more nested namespace
// names, and we expect to see an opening brace.
if (!detail::skip_if(stream, "::"))
skip(stream, "{");
auto result = cpp_namespace::builder(name.c_str(), is_inline);
auto result = cpp_namespace::builder(name.c_str(), is_inline, is_nested);
result.get().add_attribute(attributes);
return result;
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_context& context,
const CXCursor& cur)
cpp_entity& parent, const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_Namespace, detail::assert_handler{});
auto builder = make_ns_builder(context, cur);
context.comments.match(builder.get(), cur);
if (builder.get().is_nested())
{
// steal comment from parent
DEBUG_ASSERT(parent.kind() == cpp_namespace::kind(), detail::assert_handler{});
builder.get().set_comment(type_safe::copy(parent.comment()));
parent.set_comment(type_safe::nullopt);
}
else
context.comments.match(builder.get(), cur);
detail::visit_children(cur, [&](const CXCursor& cur) {
auto entity = parse_entity(context, cur);
auto entity = parse_entity(context, &builder.get(), cur);
if (entity)
builder.add_child(std::move(entity));
});

View file

@ -112,8 +112,8 @@ namespace
}
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
const CXCursor& cur,
const CXCursor& parent_cur) try
cpp_entity* parent, const CXCursor& cur,
const CXCursor& parent_cur) try
{
if (context.logger->is_verbose())
{
@ -138,7 +138,8 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
break;
case CXCursor_Namespace:
return parse_cpp_namespace(context, cur);
DEBUG_ASSERT(parent, detail::assert_handler{});
return parse_cpp_namespace(context, *parent, cur);
case CXCursor_NamespaceAlias:
return parse_cpp_namespace_alias(context, cur);
case CXCursor_UsingDirective:

View file

@ -112,7 +112,7 @@ namespace cppast
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
const CXCursor& cur);
cpp_entity& parent, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
@ -162,9 +162,10 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context,
const CXCursor& cur);
// parent: used for nested namespace, doesn't matter otherwise
// parent_cur: used when parsing templates or friends
std::unique_ptr<cpp_entity> parse_entity(
const parse_context& context, const CXCursor& cur,
const parse_context& context, cpp_entity* parent, const CXCursor& cur,
const CXCursor& parent_cur = clang_getNullCursor());
}
} // namespace cppast::detail

View file

@ -35,7 +35,7 @@ namespace
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"missing child of template");
auto entity = detail::parse_entity(context, result, cur);
auto entity = detail::parse_entity(context, nullptr, result, cur);
if (!entity)
return type_safe::nullopt;
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur,

View file

@ -33,6 +33,10 @@ namespace c
/// namespace {
/// }
namespace {}
/// namespace f{
/// }
namespace e::f {}
)";
auto file = parse({}, "cpp_namespace.cpp", code);
@ -69,10 +73,26 @@ namespace {}
REQUIRE(!ns.is_inline());
REQUIRE(no_children == 0u);
}
else if (ns.name() == "e")
{
REQUIRE(!ns.is_anonymous());
REQUIRE(!ns.is_inline());
REQUIRE(no_children == 1u);
return false; // don't have a comment
}
else if (ns.name() == "f")
{
REQUIRE(!ns.is_anonymous());
check_parent(ns, "e", "e::f");
REQUIRE(!ns.is_inline());
REQUIRE(no_children == 0u);
}
else
REQUIRE(false);
return true;
});
REQUIRE(count == 5u);
REQUIRE(count == 7u);
}
TEST_CASE("cpp_namespace_alias")
@ -103,7 +123,7 @@ namespace f = outer::c;
)";
cpp_entity_index idx;
auto check_alias = [&](const cpp_namespace_alias& alias, const char* target_name,
auto check_alias = [&](const cpp_namespace_alias& alias, const char* target_name,
const char* target_full_name, unsigned no) {
auto& target = alias.target();
REQUIRE(target.name() == target_name);