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
|
class builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// \effects Sets the namespace name and whether it is inline.
|
/// \effects Sets the namespace name and whether it is inline and nested.
|
||||||
explicit builder(std::string name, bool is_inline)
|
explicit builder(std::string name, bool is_inline, bool is_nested)
|
||||||
: namespace_(new cpp_namespace(std::move(name), is_inline))
|
: namespace_(new cpp_namespace(std::move(name), is_inline, is_nested))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +60,12 @@ namespace cppast
|
||||||
return inline_;
|
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.
|
/// \returns Whether or not the namespace is anonymous.
|
||||||
bool is_anonymous() const noexcept
|
bool is_anonymous() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -67,8 +73,8 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cpp_namespace(std::string name, bool is_inline)
|
cpp_namespace(std::string name, bool is_inline, bool is_nested)
|
||||||
: cpp_entity(std::move(name)), inline_(is_inline)
|
: cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +86,7 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inline_;
|
bool inline_;
|
||||||
|
bool nested_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \exclude
|
/// \exclude
|
||||||
|
|
@ -184,9 +191,7 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
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
|
== CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows
|
||||||
// other children due to templates and stuff
|
// other children due to templates and stuff
|
||||||
return;
|
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));
|
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
|
// for some reason libclang gives a type ref here
|
||||||
// we actually need a class decl cursor, so parse the referenced one
|
// we actually need a class decl cursor, so parse the referenced one
|
||||||
// this might be a definition, so give friend information to the parser
|
// 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("");
|
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))
|
else if (clang_isDeclaration(kind))
|
||||||
{
|
{
|
||||||
entity = parse_entity(context, child, cur);
|
entity = parse_entity(context, nullptr, child, cur);
|
||||||
if (entity)
|
if (entity)
|
||||||
{
|
{
|
||||||
// steal comment
|
// 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());
|
auto builder = cpp_language_linkage::builder(name.c_str());
|
||||||
context.comments.match(builder.get(), cur);
|
context.comments.match(builder.get(), cur);
|
||||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||||
auto entity = parse_entity(context, child);
|
auto entity = parse_entity(context, &builder.get(), child);
|
||||||
if (entity)
|
if (entity)
|
||||||
builder.add_child(std::move(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)
|
macro_iter != preprocessed.macros.end() && macro_iter->line <= line; ++macro_iter)
|
||||||
builder.add_child(std::move(macro_iter->macro));
|
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)
|
if (entity)
|
||||||
builder.add_child(std::move(entity));
|
builder.add_child(std::move(entity));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,42 +18,63 @@ namespace
|
||||||
{
|
{
|
||||||
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||||
detail::cxtoken_stream stream(tokenizer, cur);
|
detail::cxtoken_stream stream(tokenizer, cur);
|
||||||
// [inline] namespace [<attribute>] <identifier> {
|
// [inline] namespace|:: [<attribute>] <identifier> [{]
|
||||||
|
|
||||||
auto is_inline = false;
|
auto is_inline = false;
|
||||||
if (skip_if(stream, "inline"))
|
if (skip_if(stream, "inline"))
|
||||||
is_inline = true;
|
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);
|
auto attributes = parse_attributes(stream);
|
||||||
|
|
||||||
// <identifier> {
|
// <identifier> {
|
||||||
// or when anonymous: {
|
// or when anonymous: {
|
||||||
if (detail::skip_if(stream, "{"))
|
if (detail::skip_if(stream, "{"))
|
||||||
return cpp_namespace::builder("", is_inline);
|
return cpp_namespace::builder("", is_inline, false);
|
||||||
|
|
||||||
auto& name = stream.get().value();
|
auto& name = stream.get().value();
|
||||||
|
|
||||||
auto other_attributes = parse_attributes(stream);
|
auto other_attributes = parse_attributes(stream);
|
||||||
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
|
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
|
||||||
|
|
||||||
|
// 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, "{");
|
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);
|
result.get().add_attribute(attributes);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_context& context,
|
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{});
|
DEBUG_ASSERT(cur.kind == CXCursor_Namespace, detail::assert_handler{});
|
||||||
|
|
||||||
auto builder = make_ns_builder(context, cur);
|
auto builder = make_ns_builder(context, 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);
|
context.comments.match(builder.get(), cur);
|
||||||
|
|
||||||
detail::visit_children(cur, [&](const CXCursor& cur) {
|
detail::visit_children(cur, [&](const CXCursor& cur) {
|
||||||
auto entity = parse_entity(context, cur);
|
auto entity = parse_entity(context, &builder.get(), cur);
|
||||||
if (entity)
|
if (entity)
|
||||||
builder.add_child(std::move(entity));
|
builder.add_child(std::move(entity));
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
|
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
|
||||||
const CXCursor& cur,
|
cpp_entity* parent, const CXCursor& cur,
|
||||||
const CXCursor& parent_cur) try
|
const CXCursor& parent_cur) try
|
||||||
{
|
{
|
||||||
if (context.logger->is_verbose())
|
if (context.logger->is_verbose())
|
||||||
|
|
@ -138,7 +138,8 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CXCursor_Namespace:
|
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:
|
case CXCursor_NamespaceAlias:
|
||||||
return parse_cpp_namespace_alias(context, cur);
|
return parse_cpp_namespace_alias(context, cur);
|
||||||
case CXCursor_UsingDirective:
|
case CXCursor_UsingDirective:
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ namespace cppast
|
||||||
const parse_context& context, const CXCursor& cur);
|
const parse_context& context, const CXCursor& cur);
|
||||||
|
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
|
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,
|
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
|
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,
|
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
|
|
||||||
|
// parent: used for nested namespace, doesn't matter otherwise
|
||||||
// parent_cur: used when parsing templates or friends
|
// parent_cur: used when parsing templates or friends
|
||||||
std::unique_ptr<cpp_entity> parse_entity(
|
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());
|
const CXCursor& parent_cur = clang_getNullCursor());
|
||||||
}
|
}
|
||||||
} // namespace cppast::detail
|
} // namespace cppast::detail
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace
|
||||||
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
|
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
|
||||||
"missing child of template");
|
"missing child of template");
|
||||||
|
|
||||||
auto entity = detail::parse_entity(context, result, cur);
|
auto entity = detail::parse_entity(context, nullptr, result, cur);
|
||||||
if (!entity)
|
if (!entity)
|
||||||
return type_safe::nullopt;
|
return type_safe::nullopt;
|
||||||
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur,
|
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ namespace c
|
||||||
/// namespace {
|
/// namespace {
|
||||||
/// }
|
/// }
|
||||||
namespace {}
|
namespace {}
|
||||||
|
|
||||||
|
/// namespace f{
|
||||||
|
/// }
|
||||||
|
namespace e::f {}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
auto file = parse({}, "cpp_namespace.cpp", code);
|
auto file = parse({}, "cpp_namespace.cpp", code);
|
||||||
|
|
@ -69,10 +73,26 @@ namespace {}
|
||||||
REQUIRE(!ns.is_inline());
|
REQUIRE(!ns.is_inline());
|
||||||
REQUIRE(no_children == 0u);
|
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
|
else
|
||||||
REQUIRE(false);
|
REQUIRE(false);
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
REQUIRE(count == 5u);
|
REQUIRE(count == 7u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("cpp_namespace_alias")
|
TEST_CASE("cpp_namespace_alias")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue