From 6f050bac66e376916382881cc1d3b3e3b1a53688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Mon, 27 Mar 2017 21:36:55 +0200 Subject: [PATCH] Add and parse cpp_decltype_type --- include/cppast/cpp_decltype_type.hpp | 62 ++++++++++++++++++++++++++++ include/cppast/cpp_type.hpp | 2 + src/cpp_type.cpp | 2 + src/libclang/function_parser.cpp | 18 +++++--- src/libclang/type_parser.cpp | 29 ++++++++++++- src/libclang/variable_parser.cpp | 20 ++++++--- test/cpp_function.cpp | 11 ++++- test/cpp_type_alias.cpp | 20 ++++++++- test/cpp_variable.cpp | 26 +++++++++++- 9 files changed, 174 insertions(+), 16 deletions(-) create mode 100644 include/cppast/cpp_decltype_type.hpp diff --git a/include/cppast/cpp_decltype_type.hpp b/include/cppast/cpp_decltype_type.hpp new file mode 100644 index 0000000..561d32e --- /dev/null +++ b/include/cppast/cpp_decltype_type.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#ifndef CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED +#define CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED + +#include +#include + +namespace cppast +{ + /// A [cppast::cpp_type]() that isn't given but taken from an expression. + class cpp_decltype_type final : public cpp_type + { + public: + /// \returns A newly created `decltype` type. + static std::unique_ptr build(std::unique_ptr expr) + { + return std::unique_ptr(new cpp_decltype_type(std::move(expr))); + } + + /// \returns A reference to the expression given. + const cpp_expression& expression() const noexcept + { + return *expr_; + } + + private: + cpp_decltype_type(std::unique_ptr expr) : expr_(std::move(expr)) + { + } + + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::decltype_; + } + + std::unique_ptr expr_; + }; + + /// A [cppast::cpp_type]() that isn't given but deduced using the `decltype` rules. + class cpp_decltype_auto_type final : public cpp_type + { + public: + /// \returns A newly created `auto` type. + static std::unique_ptr build() + { + return std::unique_ptr(new cpp_decltype_auto_type); + } + + private: + cpp_decltype_auto_type() = default; + + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::decltype_auto; + } + }; +} // namespace cppast + +#endif // CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index deca509..c207f8f 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -19,6 +19,8 @@ namespace cppast user_defined, auto_, + decltype_, + decltype_auto, cv_qualified, pointer, diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index a8e5f96..0214adb 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -123,6 +123,8 @@ bool cppast::is_valid(const cpp_type& type) noexcept case cpp_type_kind::builtin: case cpp_type_kind::user_defined: case cpp_type_kind::auto_: + case cpp_type_kind::decltype_: + case cpp_type_kind::decltype_auto: case cpp_type_kind::template_parameter: case cpp_type_kind::template_instantiation: case cpp_type_kind::unexposed: diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 97a8463..9f6688b 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -15,17 +15,23 @@ namespace std::unique_ptr parse_parameter(const detail::parse_context& context, const CXCursor& cur) { - auto name = detail::get_cursor_name(cur); - auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); + auto name = detail::get_cursor_name(cur); + auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); + auto is_decltype = type->kind() == cpp_type_kind::decltype_; std::unique_ptr default_value; detail::visit_children(cur, [&](const CXCursor& child) { if (!clang_isExpression(clang_getCursorKind(child))) return; - - DEBUG_ASSERT(!default_value, detail::parse_error_handler{}, child, - "unexpected child cursor of function parameter"); - default_value = detail::parse_expression(context, child); + else if (is_decltype) + // skip first expression then + is_decltype = false; + else + { + DEBUG_ASSERT(!default_value, detail::parse_error_handler{}, child, + "unexpected child cursor of function parameter"); + default_value = detail::parse_expression(context, child); + } }); if (name.empty()) diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 970eb20..4f3dd76 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -5,6 +5,7 @@ #include "parse_functions.hpp" #include +#include #include #include #include @@ -434,6 +435,29 @@ namespace }); } + std::unique_ptr try_parse_decltype_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) + { + if (clang_isExpression(clang_getCursorKind(cur))) + return nullptr; // don't use decltype here + + return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr { + if (!remove_prefix(spelling, "decltype(")) + return nullptr; + + std::unique_ptr expr; + detail::visit_children(cur, [&](const CXCursor& child) { + if (!expr && clang_isExpression(clang_getCursorKind(child))) + // first expression child belongs to the decltype + expr = detail::parse_expression(context, child); + }); + DEBUG_ASSERT(expr != nullptr, detail::parse_error_handler{}, cur, + "missing child of cursor"); + + return cpp_decltype_type::build(std::move(expr)); + }); + } + std::unique_ptr parse_type_impl(const detail::parse_context& context, const CXCursor& cur, const CXType& type) { @@ -467,6 +491,9 @@ namespace else if (auto atype = try_parse_array_type(context, cur, type)) // same deal here return atype; + else if (auto dtype = try_parse_decltype_type(context, cur, type)) + // decltype unexposed + return dtype; else if (auto itype = try_parse_instantiation_type(context, cur, type)) // instantiation unexposed return itype; @@ -545,7 +572,7 @@ namespace return cpp_pointer_type::build(parse_member_pointee_type(context, cur, type)); case CXType_Auto: - return make_leave_type(type, [](std::string&&) { return cpp_auto_type::build(); }); + return make_leave_type(type, [&](std::string&&) { return cpp_auto_type::build(); }); } DEBUG_UNREACHABLE(detail::assert_handler{}); diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index 0a51d81..ffa5550 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -15,16 +15,23 @@ using namespace cppast; namespace { std::unique_ptr parse_default_value(const detail::parse_context& context, - const CXCursor& cur) + const CXCursor& cur, bool uses_decltype) { std::unique_ptr expression; detail::visit_children(cur, [&](const CXCursor& child) { if (clang_isDeclaration(clang_getCursorKind(child))) return; - DEBUG_ASSERT(clang_isExpression(child.kind) && !expression, - detail::parse_error_handler{}, cur, "unexpected child cursor of variable"); + else if (uses_decltype) + // skip first expression, belongs to the decltype + uses_decltype = false; + else + { + DEBUG_ASSERT(!expression && clang_isExpression(child.kind), + detail::parse_error_handler{}, cur, + "unexpected child cursor of variable"); - expression = detail::parse_expression(context, child); + expression = detail::parse_expression(context, child); + } }); return expression; } @@ -39,6 +46,7 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte auto type = parse_type(context, cur, clang_getCursorType(cur)); auto storage_class = get_storage_class(cur); auto is_constexpr = false; + auto uses_decltype = false; // just look for thread local or constexpr // can't appear anywhere else, so good enough @@ -49,11 +57,13 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); else if (token.value() == "constexpr") is_constexpr = true; + else if (token.value() == "decltype") + uses_decltype = true; std::unique_ptr result; if (clang_isCursorDefinition(cur)) { - auto default_value = parse_default_value(context, cur); + auto default_value = parse_default_value(context, cur, uses_decltype); result = cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), std::move(default_value), storage_class, is_constexpr); diff --git a/test/cpp_function.cpp b/test/cpp_function.cpp index d772013..34b3ef6 100644 --- a/test/cpp_function.cpp +++ b/test/cpp_function.cpp @@ -3,7 +3,9 @@ // found in the top-level directory of this distribution. #include + #include +#include #include "test_parser.hpp" @@ -15,7 +17,7 @@ TEST_CASE("cpp_function") // parameters and return type are only tested here void a(); int b(int a, float* b = nullptr); -int (&c(int a, ...))[10]; +int (&c(decltype(42) a, ...))[10]; // noexcept conditions void d() noexcept; @@ -115,7 +117,12 @@ void ns::l() { if (param.name() == "a") { - REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build("int"))); + REQUIRE( + equal_types(idx, param.type(), + *cpp_decltype_type::build( + cpp_literal_expression::build(cpp_builtin_type::build( + "int"), + "42")))); REQUIRE(!param.default_value()); } else diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index add39b4..9ef694c 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,11 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_ case cpp_type_kind::auto_: return true; + case cpp_type_kind::decltype_: + return equal_expressions(static_cast(parsed).expression(), + static_cast(synthesized).expression()); + case cpp_type_kind::decltype_auto: + return true; case cpp_type_kind::cv_qualified: { @@ -233,6 +239,9 @@ using s = int(foo::*); using t = struct t_ {}; using u = const struct u_ {}*; using v = struct {}; + +// decltype +using w = decltype(0); )"; } SECTION("typedef") @@ -279,6 +288,9 @@ typedef int(foo::*s); typedef struct t_ {} t; typedef const struct u_ {}* u; typedef struct {} v; + +// decltype +typedef decltype(0) w; )"; } @@ -456,8 +468,14 @@ typedef struct {} v; auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "v")); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } + else if (alias.name() == "w") + { + auto type = cpp_decltype_type::build( + cpp_literal_expression::build(cpp_builtin_type::build("int"), "0")); + REQUIRE(equal_types(idx, alias.underlying_type(), *type)); + } else REQUIRE(false); }); - REQUIRE(count == 22u); + REQUIRE(count == 23u); } diff --git a/test/cpp_variable.cpp b/test/cpp_variable.cpp index 81ed152..20af125 100644 --- a/test/cpp_variable.cpp +++ b/test/cpp_variable.cpp @@ -4,6 +4,8 @@ #include +#include + #include "test_parser.hpp" using namespace cppast; @@ -34,6 +36,10 @@ static struct {} l; // auto auto m = 128; const auto& n = m; + +// decltype +decltype(0) o; +const decltype(o)& p = o; )"; cpp_entity_index idx; @@ -141,10 +147,28 @@ const auto& n = m; *cpp_unexposed_expression::build(cpp_builtin_type::build("int"), "m")), cpp_storage_class_none, false, false); + else if (var.name() == "o") + check_variable(var, + *cpp_decltype_type::build( + cpp_literal_expression::build(cpp_builtin_type::build("int"), "0")), + nullptr, cpp_storage_class_none, false, false); + else if (var.name() == "p") + check_variable(var, + *cpp_reference_type:: + build(cpp_cv_qualified_type:: + build(cpp_decltype_type::build( + cpp_unexposed_expression:: + build(cpp_builtin_type::build("int"), "o")), + cpp_cv_const), + cpp_ref_lvalue), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_builtin_type::build("int"), + "o")), + cpp_storage_class_none, false, false); else REQUIRE(false); }); - REQUIRE(count == 14u); + REQUIRE(count == 16u); } TEST_CASE("static cpp_variable")