From 56d4964b61035993a994849298a0eeac4cc92e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Wed, 20 Jun 2018 09:54:16 +0200 Subject: [PATCH] [breaking] Turn macro parameters into propert entities --- include/cppast/cpp_entity_kind.hpp | 1 + include/cppast/cpp_preprocessor.hpp | 121 +++++++++++++++++++++++----- src/code_generator.cpp | 26 +++++- src/cpp_entity_kind.cpp | 6 ++ src/cpp_forward_declarable.cpp | 3 +- src/cpp_preprocessor.cpp | 10 +++ src/cpp_type.cpp | 5 +- src/libclang/preprocessor.cpp | 30 ++++++- src/visitor.cpp | 3 +- test/cpp_preprocessor.cpp | 20 ++++- 10 files changed, 194 insertions(+), 31 deletions(-) diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index f435e5b..b687114 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -14,6 +14,7 @@ namespace cppast { file_t, + macro_parameter_t, macro_definition_t, include_directive_t, diff --git a/include/cppast/cpp_preprocessor.hpp b/include/cppast/cpp_preprocessor.hpp index 58f2405..8537732 100644 --- a/include/cppast/cpp_preprocessor.hpp +++ b/include/cppast/cpp_preprocessor.hpp @@ -12,55 +12,136 @@ namespace cppast { + /// A [cppast::cpp_entity]() modelling a macro parameter. + class cpp_macro_parameter final : public cpp_entity + { + public: + static cpp_entity_kind kind() noexcept; + + /// \returns A newly built macro parameter. + /// \notes It is not meant to be registered in the [cppast::cpp_entity_index]() as no other [cppast::cpp_entity]() can refer to it. + static std::unique_ptr build(std::string name) + { + return std::unique_ptr(new cpp_macro_parameter(std::move(name))); + } + + private: + cpp_macro_parameter(std::string name) : cpp_entity(std::move(name)) {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + }; + /// A [cppast::cpp_entity]() modelling a macro definition. class cpp_macro_definition final : public cpp_entity { public: static cpp_entity_kind kind() noexcept; - /// \returns A newly built macro definition. + /// \returns A newly built object like macro. /// \notes It is not meant to be registered in the [cppast::cpp_entity_index](), /// as no other [cppast::cpp_entity]() can refer to it. - static std::unique_ptr build( - std::string name, type_safe::optional parameters, std::string replacement) + static std::unique_ptr build_object_like(std::string name, + std::string replacement) { - return std::unique_ptr( - new cpp_macro_definition(std::move(name), std::move(parameters), - std::move(replacement))); + std::unique_ptr result{new cpp_macro_definition(std::move(name))}; + result->replacement_ = std::move(replacement); + return result; } + /// Builds a function like macro. + class function_like_builder + { + public: + /// \effects Sets the name of the function like macro. + function_like_builder(std::string name) + : result_(new cpp_macro_definition(std::move(name))) + { + result_->kind_ = function_like; + } + + /// \effects Sets the replacement text. + void replacement(std::string replacement) + { + result_->replacement_ = std::move(replacement); + } + + /// \effects Marks the macro as variadic. + void is_variadic() + { + result_->kind_ = variadic_function; + } + + /// \effects Adds a parameter. + /// \group param + void parameter(std::unique_ptr param) + { + result_->parameters_.push_back(*result_, std::move(param)); + } + /// \group param + void parameter(std::string name) + { + parameter(cpp_macro_parameter::build(std::move(name))); + } + + /// \returns The finished macro. + /// \notes It is not meant to be registered in the [cppast::cpp_entity_index](), + /// as no other [cppast::cpp_entity]() can refer to it. + std::unique_ptr finish() + { + return std::move(result_); + } + + private: + std::unique_ptr result_; + }; + /// \returns The replacement text of the macro. const std::string& replacement() const noexcept { return replacement_; } + /// \returns Whether or not it is an object like macro. + bool is_object_like() const noexcept + { + return kind_ == object_like; + } + /// \returns Whether or not it is a function like macro. bool is_function_like() const noexcept { - return parameters_.has_value(); + return kind_ != object_like; } - /// \returns The parameters of the macro, as the string spelled out in the source code. - /// \notes It has none if it is not a function like macro. - const type_safe::optional& parameters() const noexcept + /// \returns Whether or not it is a variadic macro. + bool is_variadic() const noexcept { - return parameters_; + return kind_ == variadic_function; + } + + /// \returns The parameters of the macro. + /// \notes It has none if it is not a function like macro. + detail::iteratable_intrusive_list parameters() const noexcept + { + return type_safe::ref(parameters_); } private: cpp_entity_kind do_get_entity_kind() const noexcept override; - cpp_macro_definition(std::string name, type_safe::optional parameters, - std::string replacement) - : cpp_entity(std::move(name)), - parameters_(std::move(parameters)), - replacement_(std::move(replacement)) - { - } + cpp_macro_definition(std::string name) : cpp_entity(std::move(name)), kind_(object_like) {} - type_safe::optional parameters_; - std::string replacement_; + detail::intrusive_list parameters_; + std::string replacement_; + + enum : char + { + object_like, + function_like, + variadic_function, + } kind_; + + friend function_like_builder; }; /// The kind of [cppast::cpp_include_directive](). diff --git a/src/code_generator.cpp b/src/code_generator.cpp index f05eaa0..f547ac0 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -94,6 +94,15 @@ namespace return static_cast(output); } + bool generate_macro_parameter(code_generator& generator, const cpp_macro_parameter& param, + cpp_access_specifier_kind cur_access) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) + output << preprocessor_token(param.name()); + return static_cast(output); + } + bool generate_macro_definition(code_generator& generator, const cpp_macro_definition& def, cpp_access_specifier_kind cur_access) { @@ -102,9 +111,17 @@ namespace { output << preprocessor_token("#define") << whitespace << identifier(def.name()); if (def.is_function_like()) - output << preprocessor_token("(") << bracket_ws - << preprocessor_token(def.parameters().value()) << bracket_ws - << preprocessor_token(")"); + { + output << preprocessor_token("(") << bracket_ws; + auto need_sep = write_container(output, def.parameters(), comma, cpp_public); + if (def.is_variadic()) + { + if (need_sep) + output << comma; + output << preprocessor_token("..."); + } + output << bracket_ws << preprocessor_token(")"); + } if (!def.replacement().empty() && !output.options().is_set(code_generator::declaration)) output << whitespace << preprocessor_token(def.replacement()) << newl; else @@ -1059,6 +1076,7 @@ namespace CPPAST_DETAIL_HANDLE(file) + CPPAST_DETAIL_HANDLE(macro_parameter) CPPAST_DETAIL_HANDLE(macro_definition) CPPAST_DETAIL_HANDLE(include_directive) @@ -1116,7 +1134,7 @@ namespace return false; } -} +} // namespace bool code_generator::generate_code(const cpp_entity& entity) { diff --git a/src/cpp_entity_kind.cpp b/src/cpp_entity_kind.cpp index fc0fc3f..0718f78 100644 --- a/src/cpp_entity_kind.cpp +++ b/src/cpp_entity_kind.cpp @@ -13,6 +13,8 @@ const char* cppast::to_string(cpp_entity_kind kind) noexcept case cpp_entity_kind::file_t: return "file"; + case cpp_entity_kind::macro_parameter_t: + return "macro parameter"; case cpp_entity_kind::macro_definition_t: return "macro definition"; case cpp_entity_kind::include_directive_t: @@ -113,6 +115,7 @@ bool cppast::is_function(cpp_entity_kind kind) noexcept return true; case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: @@ -160,6 +163,7 @@ bool cppast::is_parameter(cpp_entity_kind kind) noexcept return true; case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: @@ -209,6 +213,7 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept return true; case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: @@ -253,6 +258,7 @@ bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept return true; case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: diff --git a/src/cpp_forward_declarable.cpp b/src/cpp_forward_declarable.cpp index c73a019..802be0b 100644 --- a/src/cpp_forward_declarable.cpp +++ b/src/cpp_forward_declarable.cpp @@ -38,6 +38,7 @@ namespace return get_declarable(*static_cast(e).begin()); case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: @@ -81,7 +82,7 @@ namespace // else lookup definition return idx.lookup_definition(declarable.value().definition().value()); } -} +} // namespace bool cppast::is_definition(const cpp_entity& e) noexcept { diff --git a/src/cpp_preprocessor.cpp b/src/cpp_preprocessor.cpp index 04329e3..9e65869 100644 --- a/src/cpp_preprocessor.cpp +++ b/src/cpp_preprocessor.cpp @@ -8,6 +8,16 @@ using namespace cppast; +cpp_entity_kind cpp_macro_parameter::kind() noexcept +{ + return cpp_entity_kind::macro_parameter_t; +} + +cpp_entity_kind cpp_macro_parameter::do_get_entity_kind() const noexcept +{ + return kind(); +} + cpp_entity_kind cpp_macro_definition::kind() noexcept { return cpp_entity_kind::macro_definition_t; diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index 3bbe123..f0834de 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -119,6 +119,7 @@ bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e) return true; case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::language_linkage_t: @@ -203,7 +204,7 @@ namespace DEBUG_UNREACHABLE(detail::assert_handler{}); return false; } -} +} // namespace bool detail::is_complex_type(const cpp_type& type) noexcept { @@ -500,7 +501,7 @@ namespace { output << token_seq(type.name()); } -} +} // namespace void detail::write_type_prefix(code_generator::output& output, const cpp_type& type) { diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index 935b871..77dc085 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -833,6 +833,34 @@ namespace return true; } + std::unique_ptr build(std::string name, ts::optional args, + std::string rep) + { + if (!args) + return cpp_macro_definition::build_object_like(std::move(name), std::move(rep)); + + cpp_macro_definition::function_like_builder builder{std::move(name)}; + builder.replacement(std::move(rep)); + + auto cur_ptr = args.value().c_str(); + auto cur_param = cur_ptr; + while (*cur_ptr) + { + while (*cur_ptr && *cur_ptr != ',') + ++cur_ptr; + + if (*cur_param == '.') + builder.is_variadic(); + else + builder.parameter(std::string(cur_param, cur_ptr)); + + if (*cur_ptr) + cur_param = ++cur_ptr; + } + + return builder.finish(); + } + std::unique_ptr parse_macro(position& p, detail::preprocessor_output& output) { @@ -878,7 +906,7 @@ namespace if (!p.write_enabled()) return nullptr; - auto result = cpp_macro_definition::build(std::move(name), std::move(args), std::move(rep)); + auto result = build(std::move(name), std::move(args), std::move(rep)); // match comment directly if (!output.comments.empty() && output.comments.back().matches(*result, cur_line)) { diff --git a/src/visitor.cpp b/src/visitor.cpp index cea76c1..f43b47e 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -59,7 +59,7 @@ namespace return cb(functor, container, {visitor_info::container_entity_exit, cur_access, last_child}); } -} +} // namespace bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, cpp_access_specifier_kind cur_access, bool last_child) @@ -91,6 +91,7 @@ bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* fun return handle_container(e, cb, functor, cur_access, last_child); + case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::include_directive_t: case cpp_entity_kind::namespace_alias_t: diff --git a/test/cpp_preprocessor.cpp b/test/cpp_preprocessor.cpp index f41015d..ad3fa40 100644 --- a/test/cpp_preprocessor.cpp +++ b/test/cpp_preprocessor.cpp @@ -40,12 +40,28 @@ namespace ns2 if (args) { REQUIRE(macro.is_function_like()); - REQUIRE(macro.parameters().value() == args); + + std::string params; + for (auto& param : macro.parameters()) + { + if (!params.empty()) + params += ","; + params += param.name(); + } + if (macro.is_variadic()) + { + if (!params.empty()) + params += ","; + params += "..."; + } + + REQUIRE(params == args); } else { REQUIRE(!macro.is_function_like()); - REQUIRE(!macro.parameters().has_value()); + REQUIRE(!macro.is_variadic()); + REQUIRE(macro.parameters().empty()); } };