diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index 2d1d769..3ed6fb3 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -129,14 +130,19 @@ namespace cppast code_generator& operator=(const code_generator&) = delete; virtual ~code_generator() noexcept = default; - /// Options that control the synopsis. - enum synopsis_options + /// Flags that control the synopsis. + enum synopsis_flags { - exclude, //< Exclude the entire entity. - declaration, //< Only write declaration. - definition, //< Also write definition. + exclude, //< Exclude the entire entity. + exclude_return, //< Exclude the return type of a function entity. + exclude_target, //< Exclude the underlying entity of an alias (e.g. typedef). + declaration, //< Only write declaration. + _flag_set_size, //< \exclude }; + /// Options that control the synopsis. + using synopsis_options = type_safe::flag_set; + /// Sentinel type used to output a given entity. class output { @@ -177,10 +183,16 @@ namespace cppast return options_ != exclude; } + /// \returns The synopsis options. + synopsis_options options() const noexcept + { + return options_; + } + /// \returns Whether or not the definition should be generated as well. bool generate_definition() const noexcept { - return options_ == definition; + return !(options_ & declaration); } /// \returns A reference to the generator. @@ -280,6 +292,14 @@ namespace cppast return *this; } + /// \effects Calls `do_write_excluded()`. + const output& excluded(const cpp_entity& e) const + { + if (*this) + gen_->do_write_excluded(e); + return *this; + } + /// \effects Calls `do_write_newline()`. const output& operator<<(newl_t) const { @@ -309,11 +329,11 @@ namespace cppast /// \effects Will be invoked before code of a container entity is generated. /// The base class version has no effect. /// \returns The synopsis options for that entity, - /// the base class version always returns `definition`. + /// the base class version always returns no special options. virtual synopsis_options on_container_begin(const cpp_entity& e) { (void)e; - return definition; + return {}; } /// \effects Will be invoked after all code of a container entity has been generated. @@ -326,11 +346,11 @@ namespace cppast /// \effects Will be invoked before code of a non-container entity is generated. /// The base class version has no effect. /// \returns The synopsis options for that entity, - /// the base class version always returns `definition`. + /// the base class version always returns no special options. virtual synopsis_options on_leaf(const cpp_entity& e) { (void)e; - return definition; + return {}; } /// \effects Will be invoked when the indentation level should be increased by one. @@ -390,6 +410,14 @@ namespace cppast do_write_token_seq(punct); } + /// \effects Writes a string for an excluded target or return type for the given entity. + /// The base class version writes the identifier `excluded`. + virtual void do_write_excluded(const cpp_entity& e) + { + (void)e; + do_write_identifier("excluded"); + } + /// \effects Writes a newline. /// It is guaranteed that this is the only way a newline will be printed. /// The base class forwards to `do_write_token_seq()`. diff --git a/src/code_generator.cpp b/src/code_generator.cpp index a54e938..fa78193 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -146,8 +146,15 @@ namespace { code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); if (output) + { output << keyword("namespace") << whitespace << identifier(alias.name()) - << punctuation("=") << alias.target() << punctuation(";") << newl; + << punctuation("="); + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + output << alias.target(); + output << punctuation(";") << newl; + } } void generate_using_directive(code_generator& generator, const cpp_using_directive& directive) @@ -175,7 +182,10 @@ namespace { output << keyword("using") << whitespace << identifier(alias.name()) << punctuation("="); - detail::write_type(output, alias.underlying_type(), ""); + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + detail::write_type(output, alias.underlying_type(), ""); output << punctuation(";") << newl; } } @@ -485,7 +495,9 @@ namespace output << keyword("friend") << whitespace; write_storage_class(output, func.storage_class(), func.is_constexpr()); - if (detail::is_complex_type(func.return_type())) + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) output << keyword("auto") << whitespace; else { @@ -504,7 +516,8 @@ namespace write_function_parameters(output, func); write_noexcept(output, func, false); - if (detail::is_complex_type(func.return_type())) + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) { output << punctuation("->"); detail::write_type(output, func.return_type(), ""); @@ -579,7 +592,9 @@ namespace else write_prefix_virtual(output, func.virtual_info()); - if (detail::is_complex_type(func.return_type())) + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) output << keyword("auto") << whitespace; else { @@ -599,7 +614,8 @@ namespace auto need_ws = write_cv_ref(output, func); write_noexcept(output, func, need_ws); - if (detail::is_complex_type(func.return_type())) + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) { output << punctuation("->"); detail::write_type(output, func.return_type(), ""); @@ -628,7 +644,11 @@ namespace auto pos = op.name().find("operator"); output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; - detail::write_type(output, op.return_type(), ""); + if (output.options() & code_generator::exclude_return) + output.excluded(op); + else + detail::write_type(output, op.return_type(), ""); + output << punctuation("(") << punctuation(")"); auto need_ws = write_cv_ref(output, op); write_noexcept(output, op, need_ws); diff --git a/test/code_generator.cpp b/test/code_generator.cpp index 8ed4108..e6e9d56 100644 --- a/test/code_generator.cpp +++ b/test/code_generator.cpp @@ -6,11 +6,15 @@ #include "test_parser.hpp" +using namespace cppast; + TEST_CASE("code_generator") { - // no need to check much here, as each entity check separately - // only write some file with equivalent code and synopsis - auto code = R"(using type=int; + SECTION("basic") + { + // no need to check much here, as each entity check separately + // only write some file with equivalent code and synopsis + auto code = R"(using type=int; struct foo{ int a; @@ -34,6 +38,60 @@ void func(int(*)(int)); extern void(* ptr)(int(*)(int))=&func; )"; - auto file = parse({}, "code_generator.cpp", code); - REQUIRE(get_code(*file) == code); + auto file = parse({}, "code_generator.cpp", code); + REQUIRE(get_code(*file) == code); + } + SECTION("exclude target") + { + auto code = R"( +namespace a {} + +namespace b = a; + +using c = int*; +typedef int d; +)"; + + auto synopsis = R"(namespace a{ +} + +namespace b=excluded; + +using c=excluded; + +using d=excluded; +)"; + + auto file = parse({}, "code_generator_exclude_target.cpp", code); + REQUIRE(get_code(*file, code_generator::exclude_target) == synopsis); + } + SECTION("exclude return") + { + auto code = R"( +void a(); +template +auto b() -> int*; + +struct foo +{ + int c() const&; + operator const int (); +}; +)"; + + auto synopsis = R"(excluded a(); + +template +excluded b(); + +struct foo{ + excluded c()const&; + + operator excluded(); +}; +)"; + + auto file = parse({}, "code_generator_exclude_return.cpp", code); + REQUIRE(get_code(*file, code_generator::exclude_return) == synopsis); + } } diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 5eedfd4..84047f8 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -45,12 +45,26 @@ inline std::unique_ptr parse(const cppast::cpp_entity_index& i class test_generator : public cppast::code_generator { public: + test_generator(synopsis_options options) : options_(std::move(options)) + { + } + const std::string& str() const noexcept { return str_; } private: + synopsis_options on_container_begin(const cppast::cpp_entity&) override + { + return options_; + } + + synopsis_options on_leaf(const cppast::cpp_entity&) override + { + return options_; + } + void do_indent() override { ++indent_; @@ -78,14 +92,16 @@ private: was_newline_ = true; } - std::string str_; - unsigned indent_ = 0; - bool was_newline_ = false; + std::string str_; + synopsis_options options_; + unsigned indent_ = 0; + bool was_newline_ = false; }; -inline std::string get_code(const cppast::cpp_entity& e) +inline std::string get_code(const cppast::cpp_entity& e, + cppast::code_generator::synopsis_options options = {}) { - test_generator generator; + test_generator generator(options); cppast::generate_code(generator, e); auto str = generator.str(); if (!str.empty() && str.back() == '\n') diff --git a/tool/main.cpp b/tool/main.cpp index 45558b1..1e656c4 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -82,14 +82,14 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e) synopsis_options on_container_begin(const cppast::cpp_entity&) override { // generate declaration only - return synopsis_options::declaration; + return code_generator::declaration; } // called before code generation of a leaf entity synopsis_options on_leaf(const cppast::cpp_entity&) override { // generate declaration only - return synopsis_options::declaration; + return code_generator::declaration; } // no need to handle indentation, as only a single line is used