Parse cpp_class
This commit is contained in:
parent
895ac5d0c9
commit
e0b76a07d0
9 changed files with 425 additions and 50 deletions
|
|
@ -37,6 +37,8 @@ namespace cppast
|
|||
class cpp_access_specifier final : public cpp_entity
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// \returns A newly created access specifier.
|
||||
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
|
||||
/// as nothing can refer to it.
|
||||
|
|
@ -66,6 +68,8 @@ namespace cppast
|
|||
class cpp_base_class final : public cpp_entity
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// \returns A newly created base class specifier.
|
||||
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
|
||||
/// as nothing can refer to the specifier itself.
|
||||
|
|
@ -111,16 +115,24 @@ namespace cppast
|
|||
class cpp_class final : public cpp_entity, public cpp_entity_container<cpp_class, cpp_entity>
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// Builds a [cppast::cpp_class]().
|
||||
class builder
|
||||
{
|
||||
public:
|
||||
/// \effects Sets the name and kind and whether it is `final`.
|
||||
explicit builder(std::string name, cpp_class_kind kind, bool is_final)
|
||||
explicit builder(std::string name, cpp_class_kind kind, bool is_final = false)
|
||||
: class_(new cpp_class(std::move(name), kind, is_final))
|
||||
{
|
||||
}
|
||||
|
||||
/// \effects Marks the class as final.
|
||||
void is_final() noexcept
|
||||
{
|
||||
class_->final_ = true;
|
||||
}
|
||||
|
||||
/// \effects Builds a [cppast::cpp_base_class]() and adds it.
|
||||
void base_class(const cpp_type_ref& base, cpp_access_specifier_kind access,
|
||||
bool is_virtual)
|
||||
|
|
|
|||
|
|
@ -46,17 +46,32 @@ const char* cppast::to_string(cpp_access_specifier_kind access) noexcept
|
|||
return "should not get here either";
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_access_specifier::do_get_entity_kind() const noexcept
|
||||
cpp_entity_kind cpp_access_specifier::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::access_specifier_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_base_class::do_get_entity_kind() const noexcept
|
||||
cpp_entity_kind cpp_access_specifier::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return kind();
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_base_class::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::base_class_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_class::do_get_entity_kind() const noexcept
|
||||
cpp_entity_kind cpp_base_class::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return kind();
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_class::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::class_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_class::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return kind();
|
||||
}
|
||||
|
|
|
|||
109
src/libclang/class_parser.cpp
Normal file
109
src/libclang/class_parser.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
#include "libclang_visitor.hpp"
|
||||
#include "parse_functions.hpp"
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
namespace
|
||||
{
|
||||
cpp_class_kind parse_class_kind(const CXCursor& cur)
|
||||
{
|
||||
switch (clang_getCursorKind(cur))
|
||||
{
|
||||
case CXCursor_ClassDecl:
|
||||
return cpp_class_kind::class_t;
|
||||
case CXCursor_StructDecl:
|
||||
return cpp_class_kind::struct_t;
|
||||
case CXCursor_UnionDecl:
|
||||
return cpp_class_kind::union_t;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
return cpp_class_kind::class_t;
|
||||
}
|
||||
|
||||
cpp_class::builder make_class_builder(const CXCursor& cur)
|
||||
{
|
||||
auto kind = parse_class_kind(cur);
|
||||
auto name = detail::cxstring(clang_getCursorSpelling(cur));
|
||||
return cpp_class::builder(name.c_str(), kind);
|
||||
}
|
||||
|
||||
cpp_access_specifier_kind convert_access(const CXCursor& cur)
|
||||
{
|
||||
switch (clang_getCXXAccessSpecifier(cur))
|
||||
{
|
||||
case CX_CXXInvalidAccessSpecifier:
|
||||
break;
|
||||
|
||||
case CX_CXXPublic:
|
||||
return cpp_public;
|
||||
case CX_CXXProtected:
|
||||
return cpp_protected;
|
||||
case CX_CXXPrivate:
|
||||
return cpp_private;
|
||||
}
|
||||
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
return cpp_public;
|
||||
}
|
||||
|
||||
void add_access_specifier(cpp_class::builder& builder, const CXCursor& cur)
|
||||
{
|
||||
DEBUG_ASSERT(cur.kind == CXCursor_CXXAccessSpecifier, detail::assert_handler{});
|
||||
builder.access_specifier(convert_access(cur));
|
||||
}
|
||||
|
||||
void add_base_class(cpp_class::builder& builder, const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{});
|
||||
auto access = convert_access(cur);
|
||||
auto is_virtual = clang_isVirtualBase(cur) != 0u;
|
||||
|
||||
detail::tokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::token_stream stream(tokenizer, cur);
|
||||
|
||||
// [<attribute>] [virtual] [<access>] <name>
|
||||
// can't use spelling to get the name
|
||||
detail::skip_attribute(stream);
|
||||
if (is_virtual)
|
||||
detail::skip(stream, "virtual");
|
||||
detail::skip_if(stream, to_string(access));
|
||||
|
||||
std::string name;
|
||||
while (!stream.done())
|
||||
name += stream.get().c_str();
|
||||
|
||||
auto type =
|
||||
cpp_type_ref(detail::get_entity_id(clang_getCursorReferenced(cur)), std::move(name));
|
||||
builder.base_class(type, access, is_virtual);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
if (!clang_isCursorDefinition(cur))
|
||||
return nullptr;
|
||||
auto builder = make_class_builder(cur);
|
||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
auto kind = clang_getCursorKind(child);
|
||||
if (kind == CXCursor_CXXAccessSpecifier)
|
||||
add_access_specifier(builder, child);
|
||||
else if (kind == CXCursor_CXXBaseSpecifier)
|
||||
add_base_class(builder, context, child);
|
||||
else if (kind == CXCursor_CXXFinalAttr)
|
||||
builder.is_final();
|
||||
else if (auto entity = parse_entity(context, child))
|
||||
builder.add_child(std::move(entity));
|
||||
});
|
||||
return builder.finish(*context.idx, get_entity_id(cur));
|
||||
}
|
||||
|
|
@ -37,9 +37,12 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
|
|||
case CXCursor_TypeAliasDecl:
|
||||
case CXCursor_TypedefDecl:
|
||||
return parse_cpp_type_alias(context, cur);
|
||||
|
||||
case CXCursor_EnumDecl:
|
||||
return parse_cpp_enum(context, cur);
|
||||
case CXCursor_ClassDecl:
|
||||
case CXCursor_StructDecl:
|
||||
case CXCursor_UnionDecl:
|
||||
return parse_cpp_class(context, cur);
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -53,9 +53,10 @@ namespace cppast
|
|||
|
||||
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_cpp_enum(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,11 @@ namespace cppast
|
|||
return str_ ? str_.value().c_str : "";
|
||||
}
|
||||
|
||||
std::string std_str() const noexcept
|
||||
{
|
||||
return c_str();
|
||||
}
|
||||
|
||||
char operator[](std::size_t i) const noexcept
|
||||
{
|
||||
return c_str()[i];
|
||||
|
|
|
|||
|
|
@ -142,17 +142,8 @@ namespace
|
|||
end = prev;
|
||||
}
|
||||
}
|
||||
else if (clang_getCursorKind(cur) == CXCursor_TypeAliasDecl
|
||||
&& !token_after_is(tu, file, cur, end, ";"))
|
||||
{
|
||||
// type alias tokens don't include everything
|
||||
do
|
||||
{
|
||||
end = get_next_location(tu, file, end);
|
||||
} while (!token_after_is(tu, file, cur, end, ";"));
|
||||
end = get_next_location(tu, file, end);
|
||||
}
|
||||
else if (clang_isExpression(clang_getCursorKind(cur)))
|
||||
else if (clang_isExpression(clang_getCursorKind(cur))
|
||||
|| clang_getCursorKind(cur) == CXCursor_CXXBaseSpecifier)
|
||||
// need to shrink range by one
|
||||
end = get_next_location(tu, file, end, -1);
|
||||
|
||||
|
|
@ -236,35 +227,46 @@ void detail::skip_brackets(detail::token_stream& stream)
|
|||
stream.set_cur(std::next(closing));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool skip_attribute_impl(detail::token_stream& stream)
|
||||
{
|
||||
if (skip_if(stream, "[") && stream.peek() == "[")
|
||||
{
|
||||
// C++11 attribute
|
||||
// [[<attribute>]]
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
// [[<attribute>]]
|
||||
// ^
|
||||
skip(stream, "]");
|
||||
return true;
|
||||
}
|
||||
else if (skip_if(stream, "__attribute__"))
|
||||
{
|
||||
// GCC/clang attributes
|
||||
// __attribute__(<attribute>)
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
return true;
|
||||
}
|
||||
else if (skip_if(stream, "__declspec"))
|
||||
{
|
||||
// MSVC declspec
|
||||
// __declspec(<attribute>)
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool detail::skip_attribute(detail::token_stream& stream)
|
||||
{
|
||||
if (skip_if(stream, "[") && stream.peek() == "[")
|
||||
{
|
||||
// C++11 attribute
|
||||
// [[<attribute>]]
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
// [[<attribute>]]
|
||||
// ^
|
||||
skip(stream, "]");
|
||||
return true;
|
||||
}
|
||||
else if (skip_if(stream, "__attribute__"))
|
||||
{
|
||||
// GCC/clang attributes
|
||||
// __attribute__(<attribute>)
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
return true;
|
||||
}
|
||||
else if (skip_if(stream, "__declspec"))
|
||||
{
|
||||
// MSVC declspec
|
||||
// __declspec(<attribute>)
|
||||
// ^
|
||||
skip_brackets(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
auto any = false;
|
||||
while (skip_attribute_impl(stream))
|
||||
any = true;
|
||||
return any;
|
||||
}
|
||||
|
|
|
|||
230
test/cpp_class.cpp
Normal file
230
test/cpp_class.cpp
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <cppast/cpp_class.hpp>
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
TEST_CASE("cpp_class")
|
||||
{
|
||||
auto code = R"(
|
||||
// basic
|
||||
struct a {};
|
||||
class b final {};
|
||||
union c {};
|
||||
|
||||
struct ignore_me;
|
||||
|
||||
// members
|
||||
struct d
|
||||
{
|
||||
enum m1 {};
|
||||
|
||||
public:
|
||||
enum m2 {};
|
||||
|
||||
private:
|
||||
private:
|
||||
enum m3 {};
|
||||
|
||||
protected:
|
||||
enum m4 {};
|
||||
};
|
||||
|
||||
// bases
|
||||
class e
|
||||
: a, private d {};
|
||||
|
||||
namespace ns
|
||||
{
|
||||
struct base {};
|
||||
}
|
||||
|
||||
struct f
|
||||
: public ns::base, virtual protected e
|
||||
{};
|
||||
|
||||
using namespace ns;
|
||||
|
||||
struct g
|
||||
: base {};
|
||||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_class.cpp", code);
|
||||
auto count = test_visit<cpp_class>(*file, [&](const cpp_class& c) {
|
||||
if (c.name() == "a" || c.name() == "base")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::struct_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.bases().begin() == c.bases().end());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
}
|
||||
else if (c.name() == "b")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::class_t);
|
||||
REQUIRE(c.is_final());
|
||||
REQUIRE(c.bases().begin() == c.bases().end());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
}
|
||||
else if (c.name() == "c")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::union_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.bases().begin() == c.bases().end());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
}
|
||||
else if (c.name() == "d")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::struct_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.bases().begin() == c.bases().end());
|
||||
|
||||
auto no_children = 0u;
|
||||
for (auto& child : c)
|
||||
{
|
||||
switch (no_children++)
|
||||
{
|
||||
case 0:
|
||||
REQUIRE(child.name() == "m1");
|
||||
break;
|
||||
case 1:
|
||||
REQUIRE(child.name() == "public");
|
||||
REQUIRE(child.kind() == cpp_entity_kind::access_specifier_t);
|
||||
REQUIRE(static_cast<const cpp_access_specifier&>(child).access_specifier()
|
||||
== cpp_public);
|
||||
break;
|
||||
case 2:
|
||||
REQUIRE(child.name() == "m2");
|
||||
break;
|
||||
case 3:
|
||||
REQUIRE(child.name() == "private");
|
||||
REQUIRE(child.kind() == cpp_entity_kind::access_specifier_t);
|
||||
REQUIRE(static_cast<const cpp_access_specifier&>(child).access_specifier()
|
||||
== cpp_private);
|
||||
break;
|
||||
case 4:
|
||||
REQUIRE(child.name() == "private");
|
||||
REQUIRE(child.kind() == cpp_entity_kind::access_specifier_t);
|
||||
REQUIRE(static_cast<const cpp_access_specifier&>(child).access_specifier()
|
||||
== cpp_private);
|
||||
break;
|
||||
case 5:
|
||||
REQUIRE(child.name() == "m3");
|
||||
break;
|
||||
case 6:
|
||||
REQUIRE(child.name() == "protected");
|
||||
REQUIRE(child.kind() == cpp_entity_kind::access_specifier_t);
|
||||
REQUIRE(static_cast<const cpp_access_specifier&>(child).access_specifier()
|
||||
== cpp_protected);
|
||||
break;
|
||||
case 7:
|
||||
REQUIRE(child.name() == "m4");
|
||||
break;
|
||||
default:
|
||||
REQUIRE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(no_children == 8u);
|
||||
}
|
||||
else if (c.name() == "e")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::class_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
|
||||
auto no_bases = 0u;
|
||||
for (auto& base : c.bases())
|
||||
{
|
||||
++no_bases;
|
||||
if (base.name() == "a")
|
||||
{
|
||||
REQUIRE(base.access_specifier() == cpp_private);
|
||||
REQUIRE(!base.is_virtual());
|
||||
|
||||
auto entity = base.entity().get(idx);
|
||||
REQUIRE(entity);
|
||||
REQUIRE(entity.value().name() == "a");
|
||||
}
|
||||
else if (base.name() == "d")
|
||||
{
|
||||
REQUIRE(base.access_specifier() == cpp_private);
|
||||
REQUIRE(!base.is_virtual());
|
||||
|
||||
auto entity = base.entity().get(idx);
|
||||
REQUIRE(entity);
|
||||
REQUIRE(entity.value().name() == "d");
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
REQUIRE(no_bases == 2u);
|
||||
}
|
||||
else if (c.name() == "f")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::struct_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
|
||||
auto no_bases = 0u;
|
||||
for (auto& base : c.bases())
|
||||
{
|
||||
++no_bases;
|
||||
if (base.name() == "ns::base")
|
||||
{
|
||||
REQUIRE(base.access_specifier() == cpp_public);
|
||||
REQUIRE(!base.is_virtual());
|
||||
|
||||
auto entity = base.entity().get(idx);
|
||||
REQUIRE(entity);
|
||||
REQUIRE(entity.value().name() == "base");
|
||||
REQUIRE(full_name(entity.value()) == "ns::base");
|
||||
}
|
||||
else if (base.name() == "e")
|
||||
{
|
||||
REQUIRE(base.access_specifier() == cpp_protected);
|
||||
REQUIRE(base.is_virtual());
|
||||
|
||||
auto entity = base.entity().get(idx);
|
||||
REQUIRE(entity);
|
||||
REQUIRE(entity.value().name() == "e");
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
REQUIRE(no_bases == 2u);
|
||||
}
|
||||
else if (c.name() == "g")
|
||||
{
|
||||
REQUIRE(c.class_kind() == cpp_class_kind::struct_t);
|
||||
REQUIRE(!c.is_final());
|
||||
REQUIRE(c.begin() == c.end());
|
||||
|
||||
auto no_bases = 0u;
|
||||
for (auto& base : c.bases())
|
||||
{
|
||||
++no_bases;
|
||||
if (base.name() == "base")
|
||||
{
|
||||
REQUIRE(base.access_specifier() == cpp_public);
|
||||
REQUIRE(!base.is_virtual());
|
||||
|
||||
auto entity = base.entity().get(idx);
|
||||
REQUIRE(entity);
|
||||
REQUIRE(entity.value().name() == "base");
|
||||
REQUIRE(full_name(entity.value()) == "ns::base");
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
REQUIRE(no_bases == 1u);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
REQUIRE(count == 8u);
|
||||
}
|
||||
|
|
@ -28,10 +28,8 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
auto user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized).entity();
|
||||
if (user_parsed.name() != user_synthesized.name())
|
||||
return false;
|
||||
return true;
|
||||
// TODO: check that the referring also works (need parsing of structs)
|
||||
// auto entity = user_parsed.get(idx);
|
||||
// return entity.has_value() && entity.value().name() == user_parsed.name();
|
||||
auto entity = user_parsed.get(idx);
|
||||
return entity.has_value() && entity.value().name() == user_parsed.name();
|
||||
}
|
||||
|
||||
case cpp_type_kind::cv_qualified:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue