Parse cpp_variable

This commit is contained in:
Jonathan Müller 2017-02-24 22:29:48 +01:00
commit 51bdbca81d
12 changed files with 281 additions and 58 deletions

View file

@ -7,7 +7,7 @@
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_storage_specifiers.hpp>
#include <cppast/cpp_storage_class_specifiers.hpp>
#include <cppast/cpp_variable_base.hpp>
namespace cppast
@ -157,7 +157,7 @@ namespace cppast
}
/// \effects Sets the storage class.
void storage_class(cpp_storage_specifiers storage)
void storage_class(cpp_storage_class_specifiers storage)
{
function->storage_ = storage;
}
@ -184,7 +184,7 @@ namespace cppast
/// \returns The [cppast::cpp_storage_specifiers]() of the function.
/// \notes If it is `cpp_storage_class_static` and inside a [cppast::cpp_class](),
/// it is a `static` class function.
cpp_storage_specifiers storage_class() const noexcept
cpp_storage_class_specifiers storage_class() const noexcept
{
return storage_;
}
@ -207,16 +207,16 @@ namespace cppast
cpp_function(std::string name, std::unique_ptr<cpp_type> ret)
: cpp_function_base(std::move(name)),
return_type_(std::move(ret)),
storage_(cpp_storage_class_none),
storage_(cpp_storage_class_auto),
friend_(false),
constexpr_(false)
{
}
std::unique_ptr<cpp_type> return_type_;
cpp_storage_specifiers storage_;
bool friend_;
bool constexpr_;
std::unique_ptr<cpp_type> return_type_;
cpp_storage_class_specifiers storage_;
bool friend_;
bool constexpr_;
};
} // namespace cppast

View file

@ -0,0 +1,53 @@
// 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.
#ifndef CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED
#define CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED
namespace cppast
{
/// C++ storage class specifiers.
///
/// See http://en.cppreference.com/w/cpp/language/storage_duration, for example.
enum cpp_storage_class_specifiers
{
cpp_storage_class_none = 0, //< no storage class specifier given.
cpp_storage_class_auto = 1, //< *automatic* storage duration.
cpp_storage_class_static =
2, //< *static* or *thread* storage duration and *internal* linkage.
cpp_storage_class_extern =
4, //< *static* or *thread* storage duration and *external* linkage.
cpp_storage_class_thread_local = 8, //< *thread* storage duration.
/// \notes This is the only one that can be combined with the others.
};
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`.
inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_thread_local) != 0;
}
/// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`.
inline bool is_auto(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_auto) != 0;
}
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`.
inline bool is_static(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_static) != 0;
}
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`.
inline bool is_extern(cpp_storage_class_specifiers spec) noexcept
{
return (spec & cpp_storage_class_extern) != 0;
}
} // namespace cppast
#endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED

View file

@ -1,40 +0,0 @@
// 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.
#ifndef CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED
#define CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED
namespace cppast
{
/// C++ storage class specifiers.
enum cpp_storage_specifiers
{
cpp_storage_class_none = 0,
cpp_storage_class_static = 1,
cpp_storage_class_extern = 2,
cpp_storage_class_thread_local = 4,
};
/// \returns Whether the [cppast::cpp_storage_specifiers]() contain `thread_local`.
inline bool is_thread_local(cpp_storage_specifiers spec) noexcept
{
return (spec & cpp_storage_class_thread_local) != 0;
}
/// \returns Whether the [cppast::cpp_storage_specifiers]() contain `static`.
inline bool is_static(cpp_storage_specifiers spec) noexcept
{
return (spec & cpp_storage_class_static) != 0;
}
/// \returns Whether the [cppast::cpp_storage_specifiers]() contain `extern`.
inline bool is_extern(cpp_storage_specifiers spec) noexcept
{
return (spec & cpp_storage_class_extern) != 0;
}
} // namespace cppast
#endif // CPPAST_CPP_STORAGE_SPECIFIERS_HPP_INCLUDED

View file

@ -6,7 +6,7 @@
#define CPPAST_CPP_VARIABLE_HPP_INCLUDED
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_storage_specifiers.hpp>
#include <cppast/cpp_storage_class_specifiers.hpp>
#include <cppast/cpp_variable_base.hpp>
namespace cppast
@ -17,15 +17,18 @@ namespace cppast
class cpp_variable final : public cpp_entity, public cpp_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered variable.
/// \notes The default value may be `nullptr` indicating no default value.
static std::unique_ptr<cpp_variable> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def,
cpp_storage_specifiers spec, bool is_constexpr);
cpp_storage_class_specifiers spec,
bool is_constexpr);
/// \returns The [cppast::cpp_storage_specifiers]() on that variable.
cpp_storage_specifiers storage_class() const noexcept
cpp_storage_class_specifiers storage_class() const noexcept
{
return storage_;
}
@ -38,7 +41,7 @@ namespace cppast
private:
cpp_variable(std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def, cpp_storage_specifiers spec,
std::unique_ptr<cpp_expression> def, cpp_storage_class_specifiers spec,
bool is_constexpr)
: cpp_entity(std::move(name)),
cpp_variable_base(std::move(type), std::move(def)),
@ -49,8 +52,8 @@ namespace cppast
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_storage_specifiers storage_;
bool is_constexpr_;
cpp_storage_class_specifiers storage_;
bool is_constexpr_;
};
} // namespace cppast

View file

@ -8,10 +8,16 @@
using namespace cppast;
cpp_entity_kind cpp_variable::kind() noexcept
{
return cpp_entity_kind::variable_t;
}
std::unique_ptr<cpp_variable> cpp_variable::build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def,
cpp_storage_specifiers spec, bool is_constexpr)
cpp_storage_class_specifiers spec,
bool is_constexpr)
{
auto result = std::unique_ptr<cpp_variable>(
new cpp_variable(std::move(name), std::move(type), std::move(def), spec, is_constexpr));
@ -21,5 +27,5 @@ std::unique_ptr<cpp_variable> cpp_variable::build(const cpp_entity_index& idx, c
cpp_entity_kind cpp_variable::do_get_entity_kind() const noexcept
{
return cpp_entity_kind::variable_t;
return kind();
}

View file

@ -32,7 +32,7 @@ namespace
cpp_class::builder make_class_builder(const CXCursor& cur)
{
auto kind = parse_class_kind(cur);
auto name = detail::cxstring(clang_getCursorSpelling(cur));
auto name = detail::get_cursor_name(cur);
return cpp_class::builder(name.c_str(), kind);
}

View file

@ -13,6 +13,11 @@ cpp_entity_id detail::get_entity_id(const CXCursor& cur)
return cpp_entity_id(usr.c_str());
}
detail::cxstring detail::get_cursor_name(const CXCursor& cur)
{
return cxstring(clang_getCursorSpelling(cur));
}
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
const CXCursor& cur) try
{
@ -44,6 +49,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_UnionDecl:
return parse_cpp_class(context, cur);
case CXCursor_VarDecl:
return parse_cpp_variable(context, cur);
default:
break;
}

View file

@ -21,6 +21,11 @@ namespace cppast
{
cpp_entity_id get_entity_id(const CXCursor& cur);
// only use this if the name is just a single token
// never where it is a reference to something (like base class name)
// as then you won't get it "as-is"
cxstring get_cursor_name(const CXCursor& cur);
struct parse_context
{
CXTranslationUnit tu;
@ -58,6 +63,9 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
}
} // namespace cppast::detail

View file

@ -412,7 +412,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_type_alias(const detail::parse_con
DEBUG_ASSERT(cur.kind == CXCursor_TypeAliasDecl || cur.kind == CXCursor_TypedefDecl,
detail::assert_handler{});
auto name = cxstring(clang_getCursorSpelling(cur));
auto name = detail::get_cursor_name(cur);
auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur));
return cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
}

View file

@ -0,0 +1,82 @@
// 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 "parse_functions.hpp"
#include <cppast/cpp_variable.hpp>
#include <clang-c/Index.h>
#include "libclang_visitor.hpp"
using namespace cppast;
namespace
{
std::unique_ptr<cpp_expression> parse_default_value(const detail::parse_context& context,
const CXCursor& cur)
{
std::unique_ptr<cpp_expression> expression;
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !expression,
detail::parse_error_handler{}, cur, "unexpected child cursor of variable");
expression = detail::parse_expression(context, child);
});
return expression;
}
cpp_storage_class_specifiers parse_storage_class(const CXCursor& cur)
{
switch (clang_Cursor_getStorageClass(cur))
{
case CX_SC_Invalid:
break;
case CX_SC_None:
return cpp_storage_class_none;
case CX_SC_Auto:
case CX_SC_Register:
return cpp_storage_class_auto;
case CX_SC_Extern:
return cpp_storage_class_extern;
case CX_SC_Static:
return cpp_storage_class_static;
case CX_SC_PrivateExtern:
case CX_SC_OpenCLWorkGroupLocal:
// non-exposed storage classes
return cpp_storage_class_auto;
}
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
return cpp_storage_class_auto;
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_VarDecl, detail::assert_handler{});
auto name = get_cursor_name(cur);
auto type = parse_type(context, clang_getCursorType(cur));
auto default_value = parse_default_value(context, cur);
auto storage_class = parse_storage_class(cur);
auto is_constexpr = false;
// just look for thread local or constexpr
// can't appear anywhere else, so good enough
detail::tokenizer tokenizer(context.tu, context.file, cur);
for (auto& token : tokenizer)
if (token.value() == "thread_local")
storage_class =
cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local);
else if (token.value() == "constexpr")
is_constexpr = true;
return cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
std::move(default_value), storage_class, is_constexpr);
}

81
test/cpp_variable.cpp Normal file
View file

@ -0,0 +1,81 @@
// 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_variable.hpp>
#include "test_parser.hpp"
using namespace cppast;
TEST_CASE("cpp_variable")
{
auto code = R"(
// basic - global scope, so external storage
int a;
unsigned long long b = 42;
float c = 3.f + 0.14f;
// with storage class specifiers
extern int d; // actually declaration
static int e;
thread_local int f;
thread_local static int g;
// constexpr
constexpr int h = 12;
)";
cpp_entity_index idx;
auto check_variable = [&](const cpp_variable& var, const cpp_type& type,
type_safe::optional_ref<const cpp_expression> default_value,
int storage_class, bool is_constexpr) {
REQUIRE(equal_types(idx, var.type(), type));
if (var.default_value())
{
REQUIRE(default_value);
REQUIRE(equal_expressions(var.default_value().value(), default_value.value()));
}
else
REQUIRE(!default_value);
REQUIRE(var.storage_class() == storage_class);
REQUIRE(var.is_constexpr() == is_constexpr);
};
auto file = parse({}, "cpp_variable.cpp", code);
auto int_type = cpp_builtin_type::build("int");
auto count = test_visit<cpp_variable>(*file, [&](const cpp_variable& var) {
INFO(var.name());
if (var.name() == "a")
check_variable(var, *int_type, nullptr, cpp_storage_class_none, false);
else if (var.name() == "b")
check_variable(var, *cpp_builtin_type::build("unsigned long long"),
// unexposed due to implicit cast, I think
*cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "42"),
cpp_storage_class_none, false);
else if (var.name() == "c")
check_variable(var, *cpp_builtin_type::build("float"),
*cpp_unexposed_expression::build(cpp_builtin_type::build("float"),
"3.f+0.14f"),
cpp_storage_class_none, false);
else if (var.name() == "d")
check_variable(var, *int_type, nullptr, cpp_storage_class_extern, false);
else if (var.name() == "e")
check_variable(var, *int_type, nullptr, cpp_storage_class_static, false);
else if (var.name() == "f")
check_variable(var, *int_type, nullptr, cpp_storage_class_thread_local, false);
else if (var.name() == "g")
check_variable(var, *int_type, nullptr,
cpp_storage_class_static | cpp_storage_class_thread_local, false);
else if (var.name() == "h")
check_variable(var, *cpp_cv_qualified_type::build(cpp_builtin_type::build("int"),
cpp_cv_const),
*cpp_literal_expression::build(cpp_builtin_type::build("int"), "12"),
cpp_storage_class_none, true);
else
REQUIRE(false);
});
REQUIRE(count == 8u);
}

View file

@ -10,6 +10,7 @@
#include <catch.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_expression.hpp>
#include <cppast/cpp_type.hpp>
#include <cppast/libclang_parser.hpp>
#include <cppast/visitor.hpp>
@ -75,4 +76,25 @@ inline void check_parent(const cppast::cpp_entity& e, const char* parent_name,
bool equal_types(const cppast::cpp_entity_index& idx, const cppast::cpp_type& parsed,
const cppast::cpp_type& synthesized);
inline bool equal_expressions(const cppast::cpp_expression& parsed,
const cppast::cpp_expression& synthesized)
{
using namespace cppast;
if (parsed.kind() != synthesized.kind())
return false;
switch (parsed.kind())
{
case cpp_expression_kind::unexposed:
return static_cast<const cpp_unexposed_expression&>(parsed).expression()
== static_cast<const cpp_unexposed_expression&>(synthesized).expression();
case cpp_expression_kind::literal:
return static_cast<const cpp_literal_expression&>(parsed).value()
== static_cast<const cpp_literal_expression&>(synthesized).value();
}
return false;
}
#endif // CPPAST_TEST_PARSER_HPP_INCLUDED