Support C++17 nested namespaces
This commit is contained in:
commit
9e8dc5dd26
10 changed files with 77 additions and 29 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue