Add exclude_return and exclude_target code generation options

This commit is contained in:
Jonathan Müller 2017-05-25 21:04:50 +02:00
commit c86e950847
5 changed files with 151 additions and 29 deletions

View file

@ -8,6 +8,7 @@
#include <cstring> #include <cstring>
#include <type_safe/index.hpp> #include <type_safe/index.hpp>
#include <type_safe/flag_set.hpp>
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_ref.hpp> #include <cppast/cpp_entity_ref.hpp>
@ -129,14 +130,19 @@ namespace cppast
code_generator& operator=(const code_generator&) = delete; code_generator& operator=(const code_generator&) = delete;
virtual ~code_generator() noexcept = default; virtual ~code_generator() noexcept = default;
/// Options that control the synopsis. /// Flags that control the synopsis.
enum synopsis_options enum synopsis_flags
{ {
exclude, //< Exclude the entire entity. 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. declaration, //< Only write declaration.
definition, //< Also write definition. _flag_set_size, //< \exclude
}; };
/// Options that control the synopsis.
using synopsis_options = type_safe::flag_set<synopsis_flags>;
/// Sentinel type used to output a given entity. /// Sentinel type used to output a given entity.
class output class output
{ {
@ -177,10 +183,16 @@ namespace cppast
return options_ != exclude; 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. /// \returns Whether or not the definition should be generated as well.
bool generate_definition() const noexcept bool generate_definition() const noexcept
{ {
return options_ == definition; return !(options_ & declaration);
} }
/// \returns A reference to the generator. /// \returns A reference to the generator.
@ -280,6 +292,14 @@ namespace cppast
return *this; 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()`. /// \effects Calls `do_write_newline()`.
const output& operator<<(newl_t) const const output& operator<<(newl_t) const
{ {
@ -309,11 +329,11 @@ namespace cppast
/// \effects Will be invoked before code of a container entity is generated. /// \effects Will be invoked before code of a container entity is generated.
/// The base class version has no effect. /// The base class version has no effect.
/// \returns The synopsis options for that entity, /// \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) virtual synopsis_options on_container_begin(const cpp_entity& e)
{ {
(void)e; (void)e;
return definition; return {};
} }
/// \effects Will be invoked after all code of a container entity has been generated. /// \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. /// \effects Will be invoked before code of a non-container entity is generated.
/// The base class version has no effect. /// The base class version has no effect.
/// \returns The synopsis options for that entity, /// \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) virtual synopsis_options on_leaf(const cpp_entity& e)
{ {
(void)e; (void)e;
return definition; return {};
} }
/// \effects Will be invoked when the indentation level should be increased by one. /// \effects Will be invoked when the indentation level should be increased by one.
@ -390,6 +410,14 @@ namespace cppast
do_write_token_seq(punct); 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. /// \effects Writes a newline.
/// It is guaranteed that this is the only way a newline will be printed. /// It is guaranteed that this is the only way a newline will be printed.
/// The base class forwards to `do_write_token_seq()`. /// The base class forwards to `do_write_token_seq()`.

View file

@ -146,8 +146,15 @@ namespace
{ {
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false);
if (output) if (output)
{
output << keyword("namespace") << whitespace << identifier(alias.name()) 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) void generate_using_directive(code_generator& generator, const cpp_using_directive& directive)
@ -175,6 +182,9 @@ namespace
{ {
output << keyword("using") << whitespace << identifier(alias.name()) output << keyword("using") << whitespace << identifier(alias.name())
<< punctuation("="); << punctuation("=");
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
detail::write_type(output, alias.underlying_type(), ""); detail::write_type(output, alias.underlying_type(), "");
output << punctuation(";") << newl; output << punctuation(";") << newl;
} }
@ -485,7 +495,9 @@ namespace
output << keyword("friend") << whitespace; output << keyword("friend") << whitespace;
write_storage_class(output, func.storage_class(), func.is_constexpr()); 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; output << keyword("auto") << whitespace;
else else
{ {
@ -504,7 +516,8 @@ namespace
write_function_parameters(output, func); write_function_parameters(output, func);
write_noexcept(output, func, false); 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("->"); output << punctuation("->");
detail::write_type(output, func.return_type(), ""); detail::write_type(output, func.return_type(), "");
@ -579,7 +592,9 @@ namespace
else else
write_prefix_virtual(output, func.virtual_info()); 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; output << keyword("auto") << whitespace;
else else
{ {
@ -599,7 +614,8 @@ namespace
auto need_ws = write_cv_ref(output, func); auto need_ws = write_cv_ref(output, func);
write_noexcept(output, func, need_ws); 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("->"); output << punctuation("->");
detail::write_type(output, func.return_type(), ""); detail::write_type(output, func.return_type(), "");
@ -628,7 +644,11 @@ namespace
auto pos = op.name().find("operator"); auto pos = op.name().find("operator");
output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace;
if (output.options() & code_generator::exclude_return)
output.excluded(op);
else
detail::write_type(output, op.return_type(), ""); detail::write_type(output, op.return_type(), "");
output << punctuation("(") << punctuation(")"); output << punctuation("(") << punctuation(")");
auto need_ws = write_cv_ref(output, op); auto need_ws = write_cv_ref(output, op);
write_noexcept(output, op, need_ws); write_noexcept(output, op, need_ws);

View file

@ -6,7 +6,11 @@
#include "test_parser.hpp" #include "test_parser.hpp"
using namespace cppast;
TEST_CASE("code_generator") TEST_CASE("code_generator")
{
SECTION("basic")
{ {
// no need to check much here, as each entity check separately // no need to check much here, as each entity check separately
// only write some file with equivalent code and synopsis // only write some file with equivalent code and synopsis
@ -37,3 +41,57 @@ extern void(* ptr)(int(*)(int))=&func;
auto file = parse({}, "code_generator.cpp", code); auto file = parse({}, "code_generator.cpp", code);
REQUIRE(get_code(*file) == 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 <typename T>
auto b() -> int*;
struct foo
{
int c() const&;
operator const int ();
};
)";
auto synopsis = R"(excluded a();
template<typename T>
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);
}
}

View file

@ -45,12 +45,26 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
class test_generator : public cppast::code_generator class test_generator : public cppast::code_generator
{ {
public: public:
test_generator(synopsis_options options) : options_(std::move(options))
{
}
const std::string& str() const noexcept const std::string& str() const noexcept
{ {
return str_; return str_;
} }
private: 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 void do_indent() override
{ {
++indent_; ++indent_;
@ -79,13 +93,15 @@ private:
} }
std::string str_; std::string str_;
synopsis_options options_;
unsigned indent_ = 0; unsigned indent_ = 0;
bool was_newline_ = false; 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); cppast::generate_code(generator, e);
auto str = generator.str(); auto str = generator.str();
if (!str.empty() && str.back() == '\n') if (!str.empty() && str.back() == '\n')

View file

@ -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 synopsis_options on_container_begin(const cppast::cpp_entity&) override
{ {
// generate declaration only // generate declaration only
return synopsis_options::declaration; return code_generator::declaration;
} }
// called before code generation of a leaf entity // called before code generation of a leaf entity
synopsis_options on_leaf(const cppast::cpp_entity&) override synopsis_options on_leaf(const cppast::cpp_entity&) override
{ {
// generate declaration only // generate declaration only
return synopsis_options::declaration; return code_generator::declaration;
} }
// no need to handle indentation, as only a single line is used // no need to handle indentation, as only a single line is used