diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index 4019b89..113ee05 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -57,6 +57,8 @@ namespace cppast class_template_t, class_template_specialization_t, + static_assert_t, + unexposed_t, count, diff --git a/include/cppast/cpp_static_assert.hpp b/include/cppast/cpp_static_assert.hpp new file mode 100644 index 0000000..1d6419f --- /dev/null +++ b/include/cppast/cpp_static_assert.hpp @@ -0,0 +1,52 @@ +// 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_STATIC_ASSERT_HPP_INCLUDED +#define CPPAST_CPP_STATIC_ASSERT_HPP_INCLUDED + +#include +#include + +namespace cppast +{ + class cpp_static_assert : public cpp_entity + { + public: + static cpp_entity_kind kind() noexcept; + + /// \returns A newly created `static_assert()` entity. + /// \notes It will not be registered as nothing can refer to it. + static std::unique_ptr build(std::unique_ptr expr, + std::string msg) + { + return std::unique_ptr( + new cpp_static_assert(std::move(expr), std::move(msg))); + } + + /// \returns A reference to the [cppast::cpp_expression]() that is being asserted. + const cpp_expression& expression() const noexcept + { + return *expr_; + } + + /// \returns A reference to the message of the assertion. + const std::string& message() const noexcept + { + return msg_; + } + + private: + cpp_static_assert(std::unique_ptr expr, std::string msg) + : cpp_entity(""), expr_(std::move(expr)), msg_(std::move(msg)) + { + } + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + std::unique_ptr expr_; + std::string msg_; + }; +} // namespace cppast + +#endif // CPPAST_CPP_STATIC_ASSERT_HPP_INCLUDED diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa2f003..07c68a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(header ../include/cppast/cpp_member_variable.hpp ../include/cppast/cpp_namespace.hpp ../include/cppast/cpp_preprocessor.hpp + ../include/cppast/cpp_static_assert.hpp ../include/cppast/cpp_storage_class_specifiers.hpp ../include/cppast/cpp_template.hpp ../include/cppast/cpp_template_parameter.hpp @@ -64,6 +65,7 @@ set(source cpp_member_variable.cpp cpp_namespace.cpp cpp_preprocessor.cpp + cpp_static_assert.cpp cpp_template_parameter.cpp cpp_type.cpp cpp_type_alias.cpp diff --git a/src/code_generator.cpp b/src/code_generator.cpp index df58213..d10e946 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -843,6 +844,18 @@ namespace } } + void generate_static_assert(code_generator& generator, const cpp_static_assert& assert) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(assert), false); + if (output) + { + output << keyword("static_assert") << punctuation("("); + detail::write_expression(output, assert.expression()); + output << punctuation(",") << string_literal('"' + assert.message() + '"'); + output << punctuation(");") << newl; + } + } + void generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity) { code_generator::output output(type_safe::ref(generator), type_safe::ref(entity), false); @@ -904,6 +917,8 @@ void cppast::generate_code(code_generator& generator, const cpp_entity& e) CPPAST_DETAIL_HANDLE(class_template) CPPAST_DETAIL_HANDLE(class_template_specialization) + CPPAST_DETAIL_HANDLE(static_assert) + case cpp_entity_kind::unexposed_t: generate_unexposed(generator, static_cast(e)); break; diff --git a/src/cpp_entity_kind.cpp b/src/cpp_entity_kind.cpp index 8af47e9..5738800 100644 --- a/src/cpp_entity_kind.cpp +++ b/src/cpp_entity_kind.cpp @@ -88,6 +88,9 @@ const char* cppast::to_string(cpp_entity_kind kind) noexcept case cpp_entity_kind::class_template_specialization_t: return "class template specialization"; + case cpp_entity_kind::static_assert_t: + return "static_assert"; + case cpp_entity_kind::unexposed_t: return "unexposed entity"; @@ -137,6 +140,7 @@ bool cppast::is_function(cpp_entity_kind kind) noexcept case cpp_entity_kind::function_template_specialization_t: case cpp_entity_kind::class_template_t: case cpp_entity_kind::class_template_specialization_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: case cpp_entity_kind::count: break; @@ -184,6 +188,7 @@ bool cppast::is_parameter(cpp_entity_kind kind) noexcept case cpp_entity_kind::function_template_specialization_t: case cpp_entity_kind::class_template_t: case cpp_entity_kind::class_template_specialization_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: case cpp_entity_kind::count: break; @@ -230,6 +235,7 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: case cpp_entity_kind::count: break; @@ -277,6 +283,7 @@ bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept case cpp_entity_kind::variable_template_t: case cpp_entity_kind::function_template_t: case cpp_entity_kind::class_template_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: case cpp_entity_kind::count: break; diff --git a/src/cpp_forward_declarable.cpp b/src/cpp_forward_declarable.cpp index 5957b6c..1041341 100644 --- a/src/cpp_forward_declarable.cpp +++ b/src/cpp_forward_declarable.cpp @@ -56,6 +56,7 @@ namespace case cpp_entity_kind::function_template_specialization_t: case cpp_entity_kind::class_template_t: case cpp_entity_kind::class_template_specialization_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: return nullptr; diff --git a/src/cpp_static_assert.cpp b/src/cpp_static_assert.cpp new file mode 100644 index 0000000..9952c40 --- /dev/null +++ b/src/cpp_static_assert.cpp @@ -0,0 +1,19 @@ +// 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. + +#include + +#include + +using namespace cppast; + +cppast::cpp_entity_kind cpp_static_assert::kind() noexcept +{ + return cpp_entity_kind::static_assert_t; +} + +cppast::cpp_entity_kind cpp_static_assert::do_get_entity_kind() const noexcept +{ + return kind(); +} diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index 3d3f670..f675ac9 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -113,6 +113,7 @@ bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e) case cpp_entity_kind::function_template_specialization_t: case cpp_entity_kind::class_template_t: case cpp_entity_kind::class_template_specialization_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: case cpp_entity_kind::count: break; diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 4d0a9e0..699e22e 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -5,6 +5,9 @@ #include "parse_functions.hpp" #include +#include + +#include "libclang_visitor.hpp" using namespace cppast; @@ -153,6 +156,9 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co case CXCursor_ClassTemplatePartialSpecialization: return parse_cpp_class_template_specialization(context, cur); + case CXCursor_StaticAssert: + return parse_cpp_static_assert(context, cur); + default: break; } @@ -178,3 +184,39 @@ catch (parse_error& ex) context.logger->log("libclang parser", ex.get_diagnostic()); return nullptr; } + +std::unique_ptr detail::parse_cpp_static_assert(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_StaticAssert, detail::assert_handler{}); + + std::unique_ptr expr; + std::string msg; + detail::visit_children(cur, [&](const CXCursor& child) { + if (!expr) + { + DEBUG_ASSERT(clang_isExpression(clang_getCursorKind(child)), + detail::parse_error_handler{}, cur, + "unexpected child cursor of static assert"); + expr = detail::parse_expression(context, child); + } + else if (msg.empty()) + { + DEBUG_ASSERT(clang_getCursorKind(child) == CXCursor_StringLiteral, + detail::parse_error_handler{}, cur, + "unexpected child cursor of static assert"); + msg = detail::get_cursor_name(child).c_str(); + DEBUG_ASSERT(msg.front() == '"' && msg.back() == '"', detail::parse_error_handler{}, + cur, "unexpected name format"); + msg.pop_back(); + msg.erase(msg.begin()); + } + else + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unexpected child cursor of static assert"); + }); + + auto result = cpp_static_assert::build(std::move(expr), std::move(msg)); + context.comments.match(*result, cur); + return result; +} diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index a50c49a..db84937 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -156,6 +156,9 @@ namespace cppast std::unique_ptr parse_cpp_class_template_specialization( const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_static_assert(const parse_context& context, + const CXCursor& cur); + // parent_cur: used when parsing templates or friends std::unique_ptr parse_entity( const parse_context& context, const CXCursor& cur, diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index 1cf1a20..3e5725c 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -183,13 +183,14 @@ namespace while (!token_after_is(tu, file, cur, end, ";")) end = get_next_location(tu, file, end); } - else if (clang_isExpression(kind) -#if CINDEX_VERSION_MINOR < 37 - || kind == CXCursor_CXXBaseSpecifier || kind == CXCursor_TemplateTypeParameter -#endif - || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl + else if (kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl || kind == CXCursor_NonTypeTemplateParameter - || kind == CXCursor_TemplateTemplateParameter) + || kind == CXCursor_TemplateTemplateParameter +#if CINDEX_VERSION_MINOR < 37 + || clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier + || kind == CXCursor_TemplateTypeParameter +#endif + ) // need to shrink range by one end = get_next_location(tu, file, end, -1); diff --git a/src/visitor.cpp b/src/visitor.cpp index 79ddc9c..78c47bf 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -93,6 +93,7 @@ bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* fun case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::template_template_parameter_t: + case cpp_entity_kind::static_assert_t: case cpp_entity_kind::unexposed_t: return cb(functor, e, {visitor_info::leaf_entity, last_child}); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc329ee..5610510 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ set(tests cpp_member_variable.cpp cpp_namespace.cpp cpp_preprocessor.cpp + cpp_static_assert.cpp cpp_template_parameter.cpp cpp_type_alias.cpp cpp_variable.cpp) diff --git a/test/cpp_static_assert.cpp b/test/cpp_static_assert.cpp new file mode 100644 index 0000000..ebd6b80 --- /dev/null +++ b/test/cpp_static_assert.cpp @@ -0,0 +1,47 @@ +// 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. + +#include + +#include "test_parser.hpp" + +using namespace cppast; + +TEST_CASE("cpp_static_assert") +{ + auto code = R"( +/// static_assert(true,""); +static_assert(true, ""); +/// static_assert(true||false,"a"); +static_assert(true || false, "a"); + +template +struct foo +{ + /// static_assert(!B,"b"); + static_assert(!B, "b"); +}; +)"; + + cpp_entity_index idx; + auto file = parse(idx, "cpp_static_assert.cpp", code); + auto count = test_visit(*file, [&](const cpp_static_assert& assert) { + auto bool_t = cpp_builtin_type::build(cpp_builtin_type_kind::cpp_bool); + + REQUIRE(assert.name().empty()); + if (assert.message() == "") + REQUIRE(equal_expressions(assert.expression(), + *cpp_literal_expression::build(std::move(bool_t), "true"))); + else if (assert.message() == "a") + REQUIRE(equal_expressions(assert.expression(), + *cpp_unexposed_expression::build(std::move(bool_t), + "true||false"))); + else if (assert.message() == "b") + REQUIRE(equal_expressions(assert.expression(), + *cpp_unexposed_expression::build(std::move(bool_t), "!B"))); + else + REQUIRE(false); + }); + REQUIRE(count == 3u); +}